From 77e44bdd4a7fcd68e3ffcc5e53eae57b04d090b3 Mon Sep 17 00:00:00 2001 From: MohamedSabthar Date: Fri, 28 Jun 2024 16:29:38 +0530 Subject: [PATCH 1/6] [Automated] Update the native jar versions --- ballerina/Ballerina.toml | 10 +++++----- ballerina/CompilerPlugin.toml | 4 ++-- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/ballerina/Ballerina.toml b/ballerina/Ballerina.toml index e49501cf9..3b12acfc2 100644 --- a/ballerina/Ballerina.toml +++ b/ballerina/Ballerina.toml @@ -1,7 +1,7 @@ [package] org = "ballerina" name = "graphql" -version = "1.13.0" +version = "1.13.1" authors = ["Ballerina"] export=["graphql", "graphql.subgraph", "graphql.dataloader"] keywords = ["gql", "network", "query", "service"] @@ -16,11 +16,11 @@ graalvmCompatible = true [[platform.java17.dependency]] groupId = "io.ballerina.stdlib" artifactId = "graphql-native" -version = "1.13.0" -path = "../native/build/libs/graphql-native-1.13.0.jar" +version = "1.13.1" +path = "../native/build/libs/graphql-native-1.13.1-SNAPSHOT.jar" [[platform.java17.dependency]] groupId = "io.ballerina.stdlib" artifactId = "graphql-commons" -version = "1.13.0" -path = "../commons/build/libs/graphql-commons-1.13.0.jar" +version = "1.13.1" +path = "../commons/build/libs/graphql-commons-1.13.1-SNAPSHOT.jar" diff --git a/ballerina/CompilerPlugin.toml b/ballerina/CompilerPlugin.toml index 2051d3c5d..d56d1d523 100644 --- a/ballerina/CompilerPlugin.toml +++ b/ballerina/CompilerPlugin.toml @@ -3,7 +3,7 @@ id = "graphql-compiler-plugin" class = "io.ballerina.stdlib.graphql.compiler.GraphqlCompilerPlugin" [[dependency]] -path = "../compiler-plugin/build/libs/graphql-compiler-plugin-1.13.0.jar" +path = "../compiler-plugin/build/libs/graphql-compiler-plugin-1.13.1-SNAPSHOT.jar" [[dependency]] -path = "../commons/build/libs/graphql-commons-1.13.0.jar" +path = "../commons/build/libs/graphql-commons-1.13.1-SNAPSHOT.jar" From 40e55be8ff33b968b6c0639b4ad5d1325d2800bb Mon Sep 17 00:00:00 2001 From: MohamedSabthar Date: Fri, 28 Jun 2024 17:08:46 +0530 Subject: [PATCH 2/6] [Automated] Update the native jar versions --- ballerina-tests/Ballerina.toml | 2 +- ballerina-tests/Dependencies.toml | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/ballerina-tests/Ballerina.toml b/ballerina-tests/Ballerina.toml index f527d8195..563951603 100644 --- a/ballerina-tests/Ballerina.toml +++ b/ballerina-tests/Ballerina.toml @@ -1,7 +1,7 @@ [package] org = "ballerina" name = "graphql_tests" -version = "1.13.0" +version = "1.13.1" [build-options] observabilityIncluded = true diff --git a/ballerina-tests/Dependencies.toml b/ballerina-tests/Dependencies.toml index 369028767..a87a69f6d 100644 --- a/ballerina-tests/Dependencies.toml +++ b/ballerina-tests/Dependencies.toml @@ -67,7 +67,7 @@ modules = [ [[package]] org = "ballerina" name = "graphql" -version = "1.13.0" +version = "1.13.1" dependencies = [ {org = "ballerina", name = "auth"}, {org = "ballerina", name = "cache"}, @@ -99,7 +99,7 @@ modules = [ [[package]] org = "ballerina" name = "graphql_tests" -version = "1.13.0" +version = "1.13.1" dependencies = [ {org = "ballerina", name = "constraint"}, {org = "ballerina", name = "file"}, From 08d4bc6985554f4cdc197a1504326ab56ece2f52 Mon Sep 17 00:00:00 2001 From: MohamedSabthar Date: Fri, 28 Jun 2024 17:16:40 +0530 Subject: [PATCH 3/6] [Automated] Update the native jar versions --- ballerina/Dependencies.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ballerina/Dependencies.toml b/ballerina/Dependencies.toml index 7256f1798..19bbeb310 100644 --- a/ballerina/Dependencies.toml +++ b/ballerina/Dependencies.toml @@ -73,7 +73,7 @@ modules = [ [[package]] org = "ballerina" name = "graphql" -version = "1.13.0" +version = "1.13.1" dependencies = [ {org = "ballerina", name = "auth"}, {org = "ballerina", name = "cache"}, From bd3b2dfee3c50413dcdf4461b21e4002374f95b5 Mon Sep 17 00:00:00 2001 From: MohamedSabthar Date: Fri, 28 Jun 2024 17:17:03 +0530 Subject: [PATCH 4/6] Remove caching every field for records with non-optional fields --- ballerina-tests/tests/44_server_caches.bal | 28 +++++++++++++++++++ ballerina-tests/tests/records.bal | 6 ++++ ...records_with_non_optional_fields_1.graphql | 11 ++++++++ ...records_with_non_optional_fields_2.graphql | 12 ++++++++ ...he_records_with_non_optional_fields_1.json | 15 ++++++++++ ...he_records_with_non_optional_fields_2.json | 18 ++++++++++++ ...he_records_with_non_optional_fields_3.json | 5 ++++ ballerina-tests/tests/test_services.bal | 27 ++++++++++++++++++ ballerina/context.bal | 22 +++------------ ballerina/engine.bal | 12 +++++--- ballerina/engine_utils.bal | 4 +++ ballerina/field.bal | 18 +++++++++--- ballerina/response_generator.bal | 5 ++-- ballerina/tests/09_cache_utils.bal | 4 +-- changelog.md | 3 ++ docs/spec/spec.md | 4 ++- .../graphql/runtime/engine/EngineUtils.java | 22 ++++++++++++--- 17 files changed, 181 insertions(+), 35 deletions(-) create mode 100644 ballerina-tests/tests/resources/documents/server_cache_records_with_non_optional_fields_1.graphql create mode 100644 ballerina-tests/tests/resources/documents/server_cache_records_with_non_optional_fields_2.graphql create mode 100644 ballerina-tests/tests/resources/expected_results/server_cache_records_with_non_optional_fields_1.json create mode 100644 ballerina-tests/tests/resources/expected_results/server_cache_records_with_non_optional_fields_2.json create mode 100644 ballerina-tests/tests/resources/expected_results/server_cache_records_with_non_optional_fields_3.json diff --git a/ballerina-tests/tests/44_server_caches.bal b/ballerina-tests/tests/44_server_caches.bal index 36fd69dba..95745a237 100644 --- a/ballerina-tests/tests/44_server_caches.bal +++ b/ballerina-tests/tests/44_server_caches.bal @@ -198,6 +198,34 @@ isolated function testServerSideCacheInOperationalLevelWithTTL() returns error? assertJsonValuesWithOrder(actualPayload, expectedPayload); } +@test:Config { + groups: ["server_cache"] +} +isolated function testCachingRecordWithoutNonOptionalFields() returns error? { + string url = "http://localhost:9091/server_cache_records_with_non_optional"; + string document = check getGraphqlDocumentFromFile("server_cache_records_with_non_optional_fields_1"); + + json actualPayload = check getJsonPayloadFromService(url, document, (), "GetProfiles"); + json expectedPayload = check getJsonContentFromFile("server_cache_records_with_non_optional_fields_1"); + assertJsonValuesWithOrder(actualPayload, expectedPayload); + + _ = check getJsonPayloadFromService(url, document, {enableEvict: false}, "RemoveProfiles"); + + actualPayload = check getJsonPayloadFromService(url, document, (), "GetProfiles"); + assertJsonValuesWithOrder(actualPayload, expectedPayload); + + document = check getGraphqlDocumentFromFile("server_cache_records_with_non_optional_fields_2"); + actualPayload = check getJsonPayloadFromService(url, document, (), "GetProfiles"); + expectedPayload = check getJsonContentFromFile("server_cache_records_with_non_optional_fields_2"); + assertJsonValuesWithOrder(actualPayload, expectedPayload); + + _ = check getJsonPayloadFromService(url, document, {enableEvict: true}, "RemoveProfiles"); + + actualPayload = check getJsonPayloadFromService(url, document, (), "GetProfiles"); + expectedPayload = check getJsonContentFromFile("server_cache_records_with_non_optional_fields_3"); + assertJsonValuesWithOrder(actualPayload, expectedPayload); +} + @test:Config { groups: ["server_cache"] } diff --git a/ballerina-tests/tests/records.bal b/ballerina-tests/tests/records.bal index 2af4bd280..7090ada47 100644 --- a/ballerina-tests/tests/records.bal +++ b/ballerina-tests/tests/records.bal @@ -407,3 +407,9 @@ type User record {| string name?; int age?; |}; + +type ProfileInfo record {| + string name; + int age; + string contact; +|}; diff --git a/ballerina-tests/tests/resources/documents/server_cache_records_with_non_optional_fields_1.graphql b/ballerina-tests/tests/resources/documents/server_cache_records_with_non_optional_fields_1.graphql new file mode 100644 index 000000000..b9c782ee5 --- /dev/null +++ b/ballerina-tests/tests/resources/documents/server_cache_records_with_non_optional_fields_1.graphql @@ -0,0 +1,11 @@ +query GetProfiles { + profiles { + name + } +} + +mutation RemoveProfiles($enableEvict: Boolean!) { + removeProfiles(enableEvict: $enableEvict) { + name + } +} diff --git a/ballerina-tests/tests/resources/documents/server_cache_records_with_non_optional_fields_2.graphql b/ballerina-tests/tests/resources/documents/server_cache_records_with_non_optional_fields_2.graphql new file mode 100644 index 000000000..d239eab4a --- /dev/null +++ b/ballerina-tests/tests/resources/documents/server_cache_records_with_non_optional_fields_2.graphql @@ -0,0 +1,12 @@ +query GetProfiles { + profiles { + name + age + } +} + +mutation RemoveProfiles($enableEvict: Boolean!) { + removeProfiles(enableEvict: $enableEvict) { + name + } +} diff --git a/ballerina-tests/tests/resources/expected_results/server_cache_records_with_non_optional_fields_1.json b/ballerina-tests/tests/resources/expected_results/server_cache_records_with_non_optional_fields_1.json new file mode 100644 index 000000000..b31e85109 --- /dev/null +++ b/ballerina-tests/tests/resources/expected_results/server_cache_records_with_non_optional_fields_1.json @@ -0,0 +1,15 @@ +{ + "data": { + "profiles": [ + { + "name": "John" + }, + { + "name": "Doe" + }, + { + "name": "Jane" + } + ] + } +} \ No newline at end of file diff --git a/ballerina-tests/tests/resources/expected_results/server_cache_records_with_non_optional_fields_2.json b/ballerina-tests/tests/resources/expected_results/server_cache_records_with_non_optional_fields_2.json new file mode 100644 index 000000000..daa4d5786 --- /dev/null +++ b/ballerina-tests/tests/resources/expected_results/server_cache_records_with_non_optional_fields_2.json @@ -0,0 +1,18 @@ +{ + "data": { + "profiles": [ + { + "name": "John", + "age": 30 + }, + { + "name": "Doe", + "age": 25 + }, + { + "name": "Jane", + "age": 35 + } + ] + } +} \ No newline at end of file diff --git a/ballerina-tests/tests/resources/expected_results/server_cache_records_with_non_optional_fields_3.json b/ballerina-tests/tests/resources/expected_results/server_cache_records_with_non_optional_fields_3.json new file mode 100644 index 000000000..5afdfa2eb --- /dev/null +++ b/ballerina-tests/tests/resources/expected_results/server_cache_records_with_non_optional_fields_3.json @@ -0,0 +1,5 @@ +{ + "data": { + "profiles": [] + } +} diff --git a/ballerina-tests/tests/test_services.bal b/ballerina-tests/tests/test_services.bal index a2b5bf448..5d3a6d3ae 100644 --- a/ballerina-tests/tests/test_services.bal +++ b/ballerina-tests/tests/test_services.bal @@ -3044,3 +3044,30 @@ service /cache_with_list_input on basicListener { return address; } } + + +@graphql:ServiceConfig { + cacheConfig: { + maxSize: 5 + } +} +service /server_cache_records_with_non_optional on basicListener { + private table profiles = table [ + {name: "John", age: 30, contact: "0123456789"}, + {name: "Doe", age: 25, contact: "9876543210"}, + {name: "Jane", age: 35, contact: "1234567890"} + ]; + + resource function get profiles() returns ProfileInfo[] { + return self.profiles.toArray(); + } + + remote function removeProfiles(graphql:Context ctx, boolean enableEvict) returns ProfileInfo[]|error { + if enableEvict { + check ctx.invalidateAll(); + } + ProfileInfo[] profiles = self.profiles.toArray(); + self.profiles.removeAll(); + return profiles; + } +} diff --git a/ballerina/context.bal b/ballerina/context.bal index 3f61a4415..417282491 100644 --- a/ballerina/context.bal +++ b/ballerina/context.bal @@ -22,10 +22,10 @@ import ballerina/lang.value; # The GraphQL context object used to pass the meta information between resolvers. public isolated class Context { - private final map attributes; - private final ErrorDetail[] errors; - private Engine? engine; - private int nextInterceptor; + private final map attributes = {}; + private final ErrorDetail[] errors = []; + private Engine? engine = (); + private int nextInterceptor = 0; private boolean hasFileInfo = false; // This field value changed by setFileInfo method private map idDataLoaderMap = {}; // Provides mapping between user defined id and DataLoader private map uuidPlaceholderMap = {}; @@ -34,20 +34,6 @@ public isolated class Context { private int unResolvedPlaceholderCount = 0; // Tracks the number of Placeholders needs to be resolved private int unResolvedPlaceholderNodeCount = 0; // Tracks the number of nodes to be replaced in the value tree - public isolated function init(map attributes = {}, Engine? engine = (), - int nextInterceptor = 0) { - self.attributes = {}; - self.engine = engine; - self.errors = []; - self.nextInterceptor = nextInterceptor; - - foreach var [key, value] in attributes.entries() { - lock { - self.attributes[key] = value is value:Cloneable ? value.cloneReadOnly() : value; - } - } - } - # Sets a given value for a given key in the GraphQL context. # # + key - The key for the value to be set diff --git a/ballerina/engine.bal b/ballerina/engine.bal index 32e3731c6..33659c46f 100644 --- a/ballerina/engine.bal +++ b/ballerina/engine.bal @@ -63,7 +63,10 @@ isolated class Engine { return self.cacheConfig; } - isolated function addToCache(string key, any value, decimal maxAge) returns any|error { + isolated function addToCache(string key, any value, decimal maxAge, boolean alreadyCached = false) returns any|error { + if alreadyCached { + return; + } cache:Cache? cache = self.cache; if cache is cache:Cache { return cache.put(key, value, maxAge); @@ -314,7 +317,7 @@ isolated class Engine { } any fieldValue; parser:RootOperationType operationType = 'field.getOperationType(); - if operationType == parser:OPERATION_QUERY && 'field.isCacheEnabled() { + if 'field.isCacheEnabled() && 'field.getOperationType() == parser:OPERATION_QUERY { string cacheName = string `${'field.getName()}.cache`; addTracingInfomation({context, serviceName: cacheName, operationType}); addFieldMetric('field); @@ -325,8 +328,9 @@ isolated class Engine { } else { fieldValue = check self.getFieldValue(context, 'field, responseGenerator); decimal maxAge = 'field.getCacheMaxAge(); - if maxAge > 0d && fieldValue !is () { - _ = check self.addToCache(cacheKey, fieldValue, maxAge); + boolean alreadyCached = 'field.isAlreadyCached(); + if !alreadyCached && maxAge > 0d && fieldValue !is () { + _ = check self.addToCache(cacheKey, fieldValue, maxAge, alreadyCached); } } } else { diff --git a/ballerina/engine_utils.bal b/ballerina/engine_utils.bal index 470e4e4d2..2e569f4a9 100644 --- a/ballerina/engine_utils.bal +++ b/ballerina/engine_utils.bal @@ -136,3 +136,7 @@ isolated function hasRecordReturnType(service object {} serviceObject, string[] returns boolean = @java:Method { 'class: "io.ballerina.stdlib.graphql.runtime.engine.Engine" } external; + +isolated function isRecordWithNoOptionalFields(any|error value) returns boolean = @java:Method { + 'class: "io.ballerina.stdlib.graphql.runtime.engine.EngineUtils" +} external; diff --git a/ballerina/field.bal b/ballerina/field.bal index e1e776a3c..eb56e92d0 100644 --- a/ballerina/field.bal +++ b/ballerina/field.bal @@ -32,11 +32,12 @@ public class Field { private final boolean cacheEnabled; private final decimal cacheMaxAge; private boolean hasRequestedNullableFields; + private final boolean alreadyCached; isolated function init(parser:FieldNode internalNode, __Type fieldType, __Type parentType, service object {}? serviceObject = (), (string|int)[] path = [], parser:RootOperationType operationType = parser:OPERATION_QUERY, string[] resourcePath = [], - any|error fieldValue = (), ServerCacheConfig? cacheConfig = (), readonly & string[] parentArgHashes = []) { + any|error fieldValue = (), ServerCacheConfig? cacheConfig = (), readonly & string[] parentArgHashes = [], boolean isAlreadyCached = false) { self.internalNode = internalNode; self.serviceObject = serviceObject; self.fieldType = fieldType; @@ -46,6 +47,7 @@ public class Field { self.resourcePath = resourcePath; self.fieldValue = fieldValue; self.resourcePath.push(internalNode.getName()); + self.alreadyCached = isAlreadyCached; self.fieldInterceptors = serviceObject is service object {} ? getFieldInterceptors(serviceObject, operationType, internalNode.getName(), self.resourcePath) : []; ServerCacheConfig? fieldCache = serviceObject is service object {} ? @@ -136,6 +138,10 @@ public class Field { return self.fieldType; } + isolated function isAlreadyCached() returns boolean { + return self.alreadyCached; + } + isolated function getFieldValue() returns any|error { return self.fieldValue; } @@ -155,9 +161,13 @@ public class Field { if selection is parser:FieldNode { foreach __Field 'field in typeFields { if 'field.name == selection.getName() { - result.push(new Field(selection, 'field.'type, parentType, (),[...currentPath, ...unwrappedPath, 'field.name], - self.operationType.clone(), self.resourcePath.clone(), cacheConfig = self.cacheConfig, - parentArgHashes = self.parentArgHashes)); + result.push(new Field(selection, 'field.'type, parentType, (), [ + ...currentPath, + ...unwrappedPath, + 'field.name + ], self.operationType.clone(), self.resourcePath.clone(), + cacheConfig = self.cacheConfig, parentArgHashes = self.parentArgHashes + )); break; } } diff --git a/ballerina/response_generator.bal b/ballerina/response_generator.bal index 91d7118aa..d3e7180bc 100644 --- a/ballerina/response_generator.bal +++ b/ballerina/response_generator.bal @@ -154,11 +154,12 @@ class ResponseGenerator { any fieldValue = parentValue.hasKey(fieldNode.getName()) ? parentValue.get(fieldNode.getName()) : (); __Type parentType = self.fieldType; __Type fieldType = getFieldTypeFromParentType(parentType, self.engine.getSchema().types, fieldNode); + boolean isAlreadyCached = isRecordWithNoOptionalFields(parentValue); (string|int)[] clonedPath = self.path.clone(); clonedPath.push(fieldNode.getAlias()); Field 'field = new (fieldNode, fieldType, parentType, path = clonedPath, fieldValue = fieldValue, - cacheConfig = self.cacheConfig, parentArgHashes = self.parentArgHashes - ); + cacheConfig = self.cacheConfig, parentArgHashes = self.parentArgHashes, + isAlreadyCached = isAlreadyCached); self.context.resetInterceptorCount(); return self.engine.resolve(self.context, 'field); } diff --git a/ballerina/tests/09_cache_utils.bal b/ballerina/tests/09_cache_utils.bal index 7a867fbed..197caf226 100644 --- a/ballerina/tests/09_cache_utils.bal +++ b/ballerina/tests/09_cache_utils.bal @@ -13,9 +13,9 @@ // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. +import graphql.parser; import ballerina/test; -import graphql.parser; @test:Config { groups: ["server_cache"] @@ -39,7 +39,7 @@ function testCacheConfigInferring() returns error? { test:assertTrue('field.getSubfields() is Field[]); Field[] subfields = 'field.getSubfields(); string[] expectedCacheKey = ["person.name.11FxOYiYfpMxmANj4kGJzg==", "person.address.nj4v+q6cUjv3W/MbZdNQXg=="]; - foreach int i in 0..**Note:** In both cases above, if the resolver returns a record that doesn't contain any optional fields, then the entire record will be cached instead of individually caching the subfields of this record. In the case of the resolver returning a record containing optional fields, all the subfields of the record will be cached individually. + #### 10.7.1.3 Cache Invalidation Since server-side caching is implemented using the Ballerina cache module, the default eviction policy will utilize the `Least Recently Used (LRU)` mechanism. In addition to LRU cache eviction, the GraphQL module provides APIs for manual cache eviction. Currently, it provides `invalidate` and `invalidateAll` APIs for manual cache eviction. These APIs can be accessed through the [graphql:Context](#101-context-object) object. diff --git a/native/src/main/java/io/ballerina/stdlib/graphql/runtime/engine/EngineUtils.java b/native/src/main/java/io/ballerina/stdlib/graphql/runtime/engine/EngineUtils.java index f3425f468..026f7bcd8 100644 --- a/native/src/main/java/io/ballerina/stdlib/graphql/runtime/engine/EngineUtils.java +++ b/native/src/main/java/io/ballerina/stdlib/graphql/runtime/engine/EngineUtils.java @@ -23,6 +23,7 @@ import io.ballerina.runtime.api.creators.ValueCreator; import io.ballerina.runtime.api.flags.SymbolFlags; import io.ballerina.runtime.api.types.ArrayType; +import io.ballerina.runtime.api.types.Field; import io.ballerina.runtime.api.types.IntersectionType; import io.ballerina.runtime.api.types.RecordType; import io.ballerina.runtime.api.types.ResourceMethodType; @@ -106,10 +107,6 @@ private EngineUtils() { public static final BString HAS_FILE_INFO_FIELD = StringUtils.fromString("hasFileInfo"); public static final BString RESULT_FIELD = StringUtils.fromString("result"); - // Entity annotation fields - private static final BString ENTITY_ANNOTATION_KEY_FIELD = StringUtils.fromString("key"); - private static final BString ENTITY_ANNOTATION_RESOLVER_FIELD = StringUtils.fromString("resolveReference"); - // Resource annotation public static final String RESOURCE_CONFIG = "ResourceConfig"; public static final String COLON = ":"; @@ -201,6 +198,23 @@ public static BString getTypeNameFromValue(BValue bValue) { return StringUtils.fromString(""); } + public static boolean isRecordWithNoOptionalFields(Object value) { + if (value instanceof BMap) { + BMap recordValue = (BMap) value; + Type type = TypeUtils.getImpliedType(recordValue.getType()); + if (type.getTag() == TypeTags.RECORD_TYPE_TAG) { + RecordType recordType = (RecordType) type; + for (Field field : recordType.getFields().values()) { + if (SymbolFlags.isFlagOn(field.getFlags(), SymbolFlags.OPTIONAL)) { + return false; + } + } + return true; + } + } + return false; + } + static String getTypeNameFromRecordValue(RecordType recordType) { if (recordType.getName().contains("&") && recordType.getIntersectionType().isPresent()) { for (Type constituentType : recordType.getIntersectionType().get().getConstituentTypes()) { From a860c7b4596f6f888ba4836cbcb94b55337e9d6f Mon Sep 17 00:00:00 2001 From: MohamedSabthar Date: Fri, 28 Jun 2024 17:21:58 +0530 Subject: [PATCH 5/6] Update test resources --- .../server_cache_records_with_non_optional_fields_1.json | 2 +- .../server_cache_records_with_non_optional_fields_2.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ballerina-tests/tests/resources/expected_results/server_cache_records_with_non_optional_fields_1.json b/ballerina-tests/tests/resources/expected_results/server_cache_records_with_non_optional_fields_1.json index b31e85109..ccc31cb20 100644 --- a/ballerina-tests/tests/resources/expected_results/server_cache_records_with_non_optional_fields_1.json +++ b/ballerina-tests/tests/resources/expected_results/server_cache_records_with_non_optional_fields_1.json @@ -12,4 +12,4 @@ } ] } -} \ No newline at end of file +} diff --git a/ballerina-tests/tests/resources/expected_results/server_cache_records_with_non_optional_fields_2.json b/ballerina-tests/tests/resources/expected_results/server_cache_records_with_non_optional_fields_2.json index daa4d5786..8a8e3b51c 100644 --- a/ballerina-tests/tests/resources/expected_results/server_cache_records_with_non_optional_fields_2.json +++ b/ballerina-tests/tests/resources/expected_results/server_cache_records_with_non_optional_fields_2.json @@ -15,4 +15,4 @@ } ] } -} \ No newline at end of file +} From c1fb468ca16b44491840f72b986dd20d0324b442 Mon Sep 17 00:00:00 2001 From: MohamedSabthar Date: Fri, 28 Jun 2024 17:47:16 +0530 Subject: [PATCH 6/6] Update changelog.md Co-authored-by: DimuthuMadushan <35717653+DimuthuMadushan@users.noreply.github.com> --- changelog.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/changelog.md b/changelog.md index 4eb54e7e7..836dcad1b 100644 --- a/changelog.md +++ b/changelog.md @@ -7,7 +7,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), ## [Unreleased] ### Changed -[[#6652] Cache Entire Record Having All Non-Optional Fields Instead of Caching Each Field Separately](https://github.com/ballerina-platform/ballerina-library/issues/6652) +- [[#6652] Cache Entire Record Having All Non-Optional Fields Instead of Caching Each Field Separately](https://github.com/ballerina-platform/ballerina-library/issues/6652) ## [1.13.0] - 2024-05-06