Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

bugfix/backing store #481

Merged
merged 13 commits into from
Aug 16, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Changed

- Fixed a bug where raw collections requests would not be supported #467
- Fixes a bug where in memory backing store would not return changed properties to null #243
- Fixes a bug where generated models would be tied to a specific backing store implementation #400

## [0.0.7] - 2021-08-04

Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ Kiota accepts the following parameters during the generation:

| Name | Shorthand | Required | Description | Accepted values | Default Value |
| ---- | --------- | -------- | ----------- | --------------- | ------------- |
| backing-store | b | no | The fully qualified name for the backing store class to use. | A fully qualified class name like `Microsoft.Kiota.Abstractions.Store.InMemoryBackingStore` (CSharp), `com.microsoft.kiota.store.InMemoryBackingStore` (Java), `@microsoft/kiota-abstractions.InMemoryBackingStore` (TypeScript) | Empty string |
| backing-store | b | no | Enables backing store for models. | Flag. N/A. | false |
| class-name | c | no | The class name to use the for main entry point | A valid class name according to the target language specification. | ApiClient |
| deserializer | ds | no | The fully qualified class names for deserializers. | This parameter can be passed multiple values. A module name like `Microsoft.Kiota.Serialization.Json` that implementats `IParseNodeFactory`. | `Microsoft.Kiota.Serialization.Json.JsonParseNodeFactory` (csharp), `@microsoft/kiota-serialization-json.JsonParseNodeFactory` (typescript), `com.microsoft.kiota.serialization.JsonParseNodeFactory` (java) |
| language | l | no | The programming language to generate the SDK in. | csharp, java, or typescript | csharp |
Expand Down
4 changes: 3 additions & 1 deletion abstractions/dotnet/src/IHttpCore.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.Kiota.Abstractions.Serialization;
using Microsoft.Kiota.Abstractions.Store;

namespace Microsoft.Kiota.Abstractions {
/// <summary>
Expand All @@ -10,7 +11,8 @@ public interface IHttpCore {
/// <summary>
/// Enables the backing store proxies for the SerializationWriters and ParseNodes in use.
/// </summary>
void EnableBackingStore();
/// <param name="backingStoreFactory">The backing store factory to use.</param>
void EnableBackingStore(IBackingStoreFactory backingStoreFactory);
/// <summary>
/// Gets the serialization writer factory currently in use for the HTTP core service.
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
<TargetFramework>net5.0</TargetFramework>
<PublishRepositoryUrl>true</PublishRepositoryUrl>
<RepositoryUrl>https://github.com/microsoft/kiota</RepositoryUrl>
<Version>1.0.15</Version>
<Version>1.0.16</Version>
</PropertyGroup>

</Project>
2 changes: 1 addition & 1 deletion abstractions/dotnet/src/RequestInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ public class RequestInfo
/// </summary>
/// <param name="middlewareOption">The middleware option to add.</param>
public void AddMiddlewareOptions(params IMiddlewareOption[] options) {
if(!options?.Any() ?? false) return; // it's a no-op if there are no options and this avoid having to check in the code gen.
if(!(options?.Any() ?? false)) return; // it's a no-op if there are no options and this avoid having to check in the code gen.
foreach(var option in options.Where(x => x != null))
if(!_middlewareOptions.TryAdd(option.GetType().FullName, option))
_middlewareOptions[option.GetType().FullName] = option;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,11 @@ public interface ISerializationWriter : IDisposable {
/// <param name="value">The enum value to be written.</param>
void WriteEnumValue<T>(string key, T? value) where T : struct, Enum;
/// <summary>
/// Writes a null value for the specified key.
/// </summary>
/// <param name="key">The key to be used for the written value. May be null.</param>
void WriteNullValue(string key);
/// <summary>
/// Writes the specified additional data to the stream.
/// </summary>
/// <param name="data">The additional data to be written.</param>
Expand All @@ -91,5 +96,9 @@ public interface ISerializationWriter : IDisposable {
/// Callback called after the serialization process ends.
/// </summary>
Action<IParsable> OnAfterObjectSerialization { get; set; }
/// <summary>
/// Callback called right after the serialization process starts.
/// </summary>
Action<IParsable, ISerializationWriter> OnStartObjectSerialization { get; set; }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,22 +9,28 @@ public class SerializationWriterProxyFactory : ISerializationWriterFactory {
private readonly ISerializationWriterFactory _concrete;
private readonly Action<IParsable> _onBefore;
private readonly Action<IParsable> _onAfter;
private readonly Action<IParsable, ISerializationWriter> _onStartSerialization;
/// <summary>
/// Creates a new proxy factory that wraps the specified concrete factory while composing the before and after callbacks.
/// </summary>
/// <param name="concrete">The concrete factory to wrap.</param>
/// <param name="onBefore">The callback to invoke before the serialization of any model object.</param>
/// <param name="onAfter">The callback to invoke after the serialization of any model object.</param>
/// <param name="onBeforeSerialization">The callback to invoke before the serialization of any model object.</param>
/// <param name="onAfterSerialization">The callback to invoke after the serialization of any model object.</param>
/// <param name="onStartSerialization">The callback to invoke when serialization of the entire model has started.</param>
public SerializationWriterProxyFactory(ISerializationWriterFactory concrete,
Action<IParsable> onBeforeSerialization, Action<IParsable> onAfterSerialization) {
Action<IParsable> onBeforeSerialization,
Action<IParsable> onAfterSerialization,
Action<IParsable, ISerializationWriter> onStartSerialization) {
_concrete = concrete ?? throw new ArgumentNullException(nameof(concrete));
_onBefore = onBeforeSerialization;
_onAfter = onAfterSerialization;
_onStartSerialization = onStartSerialization;
}
public ISerializationWriter GetSerializationWriter(string contentType) {
var writer = _concrete.GetSerializationWriter(contentType);
var originalBefore = writer.OnBeforeObjectSerialization;
var originalAfter = writer.OnAfterObjectSerialization;
var originalStart = writer.OnStartObjectSerialization;
writer.OnBeforeObjectSerialization = (x) => {
_onBefore?.Invoke(x); // the callback set by the implementation (e.g. backing store)
originalBefore?.Invoke(x); // some callback that might already be set on the target
Expand All @@ -33,6 +39,10 @@ public ISerializationWriter GetSerializationWriter(string contentType) {
_onAfter?.Invoke(x);
originalAfter?.Invoke(x);
};
writer.OnStartObjectSerialization = (x, y) => {
_onStartSerialization?.Invoke(x, y);
originalStart?.Invoke(x, y);
};
return writer;
}
}
Expand Down
11 changes: 11 additions & 0 deletions abstractions/dotnet/src/store/BackingStoreFactorySingleton.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
namespace Microsoft.Kiota.Abstractions.Store {
/// <summary>
/// This class is used to register the backing store factory.
/// </summary>
public class BackingStoreFactorySingleton {
/// <summary>
/// The backing store factory singleton instance.
/// </summary>
public static IBackingStoreFactory Instance { get; set; } = new InMemoryBackingStoreFactory();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,12 @@ public BackingStoreSerializationWriterProxyFactory(ISerializationWriterFactory c
backedModel.BackingStore.ReturnOnlyChangedValues = false;
backedModel.BackingStore.InitializationCompleted = true;
}
},(x, y) => {
if(x is IBackedModel backedModel && backedModel.BackingStore != null) {
var nullValues = backedModel.BackingStore.EnumerateKeysForValuesChangedToNull();
foreach(var key in nullValues)
y.WriteNullValue(key);
}
}) {}
}
}
5 changes: 5 additions & 0 deletions abstractions/dotnet/src/store/IBackingStore.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,11 @@ public interface IBackingStore {
/// <returns>The values available in the backing store.</returns>
IEnumerable<KeyValuePair<string, object>> Enumerate();
/// <summary>
/// Enumerates the keys for all values that changed to null.
/// </summary>
/// <returns>The keys for all values that changed to null.</returns>
IEnumerable<string> EnumerateKeysForValuesChangedToNull();
/// <summary>
/// Creates a subscription to any data change happening.
/// </summary>
/// <param name="callback">Callback to be invoked on data changes where the first parameter is the data key, the second the previous value and the third the new value.</param>
Expand Down
12 changes: 12 additions & 0 deletions abstractions/dotnet/src/store/IBackingStoreFactory.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
namespace Microsoft.Kiota.Abstractions.Store {
/// <summary>
/// Defines the contract for a factory that creates backing stores.
/// </summary>
public interface IBackingStoreFactory {
/// <summary>
/// Creates a new instance of the backing store.
/// </summary>
/// <returns>A new instance of the backing store.</returns>
IBackingStore CreateBackingStore();
}
}
3 changes: 3 additions & 0 deletions abstractions/dotnet/src/store/InMemoryBackingStore.cs
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,9 @@ public IEnumerable<KeyValuePair<string, object>> Enumerate() {
return (ReturnOnlyChangedValues ? store.Where(x => !x.Value.Item1) : store)
.Select(x => new KeyValuePair<string, object>(x.Key, x.Value.Item2));
}
public IEnumerable<string> EnumerateKeysForValuesChangedToNull() {
return store.Where(x => x.Value.Item1 && x.Value.Item2 == null).Select(x => x.Key);
}
public string Subscribe(Action<string, object, object> callback) {
var id = Guid.NewGuid().ToString();
Subscribe(callback, id);
Expand Down
10 changes: 10 additions & 0 deletions abstractions/dotnet/src/store/InMemoryBackingStoreFactory.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
namespace Microsoft.Kiota.Abstractions.Store {
/// <summary>
/// This class is used to create instances of <see cref="InMemoryBackingStore" />.
/// </summary>
public class InMemoryBackingStoreFactory : IBackingStoreFactory {
public IBackingStore CreateBackingStore() {
return new InMemoryBackingStore();
}
}
}
2 changes: 1 addition & 1 deletion abstractions/java/lib/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ publishing {
publications {
gpr(MavenPublication) {
artifactId 'kiota-abstractions'
version '1.0.15'
version '1.0.16'
from(components.java)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,15 @@

import com.microsoft.kiota.serialization.Parsable;
import com.microsoft.kiota.serialization.SerializationWriterFactory;
import com.microsoft.kiota.store.BackingStoreFactory;

/** Service responsible for translating abstract Request Info into concrete native HTTP requests. */
public interface HttpCore {
/** Enables the backing store proxies for the SerializationWriters and ParseNodes in use. */
void enableBackingStore();
/**
* Enables the backing store proxies for the SerializationWriters and ParseNodes in use.
* @param backingStoreFactory The backing store factory to use.
*/
void enableBackingStore(@Nullable final BackingStoreFactory backingStoreFactory);
/**
* Gets the serialization writer factory currently in use for the HTTP core service.
* @return the serialization writer factory currently in use for the HTTP core service.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import java.util.EnumSet;
import java.lang.Enum;
import java.util.function.Consumer;
import java.util.function.BiConsumer;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;
Expand Down Expand Up @@ -92,6 +93,11 @@ public interface SerializationWriter extends Closeable {
* @param value the value to write to the stream.
*/
<T extends Enum<T>> void writeEnumValue(@Nullable final String key, @Nullable final T value);
/**
* Writes a null value for the specified key.
* @param key the key to write the value with.
*/
void writeNullValue(@Nullable final String key);
/**
* Writes the specified additional data values to the stream with an optional given key.
* @param value the values to write to the stream.
Expand All @@ -109,6 +115,12 @@ public interface SerializationWriter extends Closeable {
*/
@Nullable
Consumer<Parsable> getOnAfterObjectSerialization();
/**
* Gets the callback called right after the serialization process starts.
* @return the callback called right after the serialization process starts.
*/
@Nullable
BiConsumer<Parsable, SerializationWriter> getOnStartObjectSerialization();
/**
* Sets the callback called before the objects gets serialized.
* @param value the callback called before the objects gets serialized.
Expand All @@ -119,4 +131,9 @@ public interface SerializationWriter extends Closeable {
* @param value the callback called after the objects gets serialized.
*/
void setOnAfterObjectSerialization(@Nullable final Consumer<Parsable> value);
/**
* Sets the callback called right after the serialization process starts.
* @param value the callback called right after the serialization process starts.
*/
void setOnStartObjectSerialization(@Nullable final BiConsumer<Parsable, SerializationWriter> value);
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.microsoft.kiota.serialization;

import java.util.function.Consumer;
import java.util.function.BiConsumer;
import java.util.Objects;

import javax.annotation.Nonnull;
Expand All @@ -14,22 +15,28 @@ public String getValidContentType() {
private final SerializationWriterFactory _concrete;
private final Consumer<Parsable> _onBefore;
private final Consumer<Parsable> _onAfter;
private final BiConsumer<Parsable, SerializationWriter> _onStart;
/**
* Creates a new proxy factory that wraps the specified concrete factory while composing the before and after callbacks.
* @param concreteFactory the concrete factory to wrap
* @param onBefore the callback to invoke before the serialization of any model object.
* @param onAfter the callback to invoke after the serialization of any model object.
* @param onBeforeSerialization the callback to invoke before the serialization of any model object.
* @param onAfterSerialization the callback to invoke after the serialization of any model object.
* @param onStartSerialization the callback to invoke when the serialization of a model object starts.
*/
public SerializationWriterProxyFactory(@Nonnull final SerializationWriterFactory concrete,
@Nullable final Consumer<Parsable> onBeforeSerialization, @Nullable final Consumer<Parsable> onAfterSerialization) {
@Nullable final Consumer<Parsable> onBeforeSerialization,
@Nullable final Consumer<Parsable> onAfterSerialization,
@Nullable final BiConsumer<Parsable, SerializationWriter> onStartObjectSerialization) {
_concrete = Objects.requireNonNull(concrete);
_onBefore = onBeforeSerialization;
_onAfter = onAfterSerialization;
_onStart = onStartObjectSerialization;
}
public SerializationWriter getSerializationWriter(final String contentType) {
final SerializationWriter writer = _concrete.getSerializationWriter(contentType);
final Consumer<Parsable> originalBefore = writer.getOnBeforeObjectSerialization();
final Consumer<Parsable> originalAfter = writer.getOnAfterObjectSerialization();
final BiConsumer<Parsable, SerializationWriter> originalStart = writer.getOnStartObjectSerialization();
writer.setOnBeforeObjectSerialization((x) -> {
if(_onBefore != null) {
_onBefore.accept(x); // the callback set by the implementation (e.g. backing store)
Expand All @@ -46,6 +53,14 @@ public SerializationWriter getSerializationWriter(final String contentType) {
originalAfter.accept(x);
}
});
writer.setOnStartObjectSerialization((x, y) -> {
if(_onStart != null) {
_onStart.accept(x, y);
}
if(originalStart != null) {
originalStart.accept(x, y);
}
});
return writer;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,12 @@ public interface BackingStore {
@Nonnull
Map<String, Object> enumerate();
/**
* Enumerates the keys for all values that changed to null.
* @return The keys for the values that changed to null.
*/
@Nonnull
Iterable<String> enumerateKeysForValuesChangedToNull();
/**
* Creates a subscription to any data change happening.
* @param callback Callback to be invoked on data changes where the first parameter is the data key, the second the previous value and the third the new value.
* @return The subscription Id to use when removing the subscription
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package com.microsoft.kiota.store;

import javax.annotation.Nonnull;

/** Defines the contract for a factory that creates backing stores. */
public interface BackingStoreFactory {
/**
* Creates a new instance of the backing store.
* @return a new instance of the backing store.
*/
@Nonnull
BackingStore createBackingStore();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package com.microsoft.kiota.store;

/** This class is used to register the backing store factory. */
public class BackingStoreFactorySingleton {
/** The backing store factory singleton instance. */
public static BackingStoreFactory instance = new InMemoryBackingStoreFactory();
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,16 +15,29 @@ public BackingStoreSerializationWriterProxyFactory(@Nonnull final SerializationW
(x) -> {
if(x instanceof BackedModel) {
final BackedModel backedModel = (BackedModel)x;
if(backedModel.getBackingStore() != null) {
backedModel.getBackingStore().setReturnOnlyChangedValues(true);
final var backingStore = backedModel.getBackingStore();
if(backingStore != null) {
backingStore.setReturnOnlyChangedValues(true);
}
}
},(x) -> {
if(x instanceof BackedModel) {
final BackedModel backedModel = (BackedModel)x;
if(backedModel.getBackingStore() != null) {
backedModel.getBackingStore().setReturnOnlyChangedValues(false);
backedModel.getBackingStore().setIsInitializationCompleted(true);
final var backingStore = backedModel.getBackingStore();
if(backingStore != null) {
backingStore.setReturnOnlyChangedValues(false);
backingStore.setIsInitializationCompleted(true);
}
}
}, (x, y) -> {
if(x instanceof BackedModel) {
final BackedModel backedModel = (BackedModel)x;
final var backingStore = backedModel.getBackingStore();
if(backingStore != null) {
final var keys = backingStore.enumerateKeysForValuesChangedToNull();
for(final var key : keys) {
y.writeNullValue(key);
}
}
}
});
Expand Down
Loading