Skip to content

Commit b020443

Browse files
author
David Lindbom
committed
fix: avoid omitting boolean values when serializing
1 parent c7e19f1 commit b020443

10 files changed

Lines changed: 118 additions & 34 deletions

api/v1alpha1/apikey_types.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,15 +44,15 @@ type APIKeySpec struct {
4444

4545
// Revoke indicates whether this API key should be revoked
4646
// +optional
47-
Revoke bool `json:"revoke,omitempty"`
47+
Revoke bool `json:"revoke"`
4848

4949
// EncryptionKey contains the public key used to encrypt the token
5050
// +optional
5151
EncryptionKey *EncryptionKey `json:"encryptionKey,omitempty"`
5252

5353
// ExportPlaintextToken indicates whether the token should be exported in plaintext
5454
// +optional
55-
ExportPlaintextToken *bool `json:"exportPlaintextToken,omitempty"`
55+
ExportPlaintextToken *bool `json:"exportPlaintextToken"`
5656
}
5757

5858
// EncryptionKey contains a public key used for encryption

api/v1alpha1/common.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -170,7 +170,7 @@ type ProducerConfig struct {
170170
MaxPendingMessagesAcrossPartitions int `json:"maxPendingMessagesAcrossPartitions,omitempty" yaml:"maxPendingMessagesAcrossPartitions"`
171171

172172
// +optional
173-
UseThreadLocalProducers bool `json:"useThreadLocalProducers,omitempty" yaml:"useThreadLocalProducers"`
173+
UseThreadLocalProducers bool `json:"useThreadLocalProducers" yaml:"useThreadLocalProducers"`
174174

175175
// +optional
176176
CryptoConfig *CryptoConfig `json:"cryptoConfig,omitempty" yaml:"cryptoConfig"`
@@ -191,7 +191,7 @@ type ConsumerConfig struct {
191191
SerdeClassName string `json:"serdeClassName,omitempty" yaml:"serdeClassName"`
192192

193193
// +optional
194-
RegexPattern bool `json:"regexPattern,omitempty" yaml:"regexPattern"`
194+
RegexPattern bool `json:"regexPattern" yaml:"regexPattern"`
195195

196196
// +optional
197197
ReceiverQueueSize int `json:"receiverQueueSize,omitempty" yaml:"receiverQueueSize"`
@@ -206,7 +206,7 @@ type ConsumerConfig struct {
206206
CryptoConfig *CryptoConfig `json:"cryptoConfig,omitempty" yaml:"cryptoConfig"`
207207

208208
// +optional
209-
PoolMessages bool `json:"poolMessages,omitempty" yaml:"poolMessages"`
209+
PoolMessages bool `json:"poolMessages" yaml:"poolMessages"`
210210
}
211211

212212
// CryptoConfig represents the configuration for the crypto of the pulsar functions and connectors

api/v1alpha1/computeflinkdeployment_types.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -378,7 +378,7 @@ type ComputeFlinkDeploymentList struct {
378378

379379
// VvpRestoreStrategy defines the restore strategy of the deployment
380380
type VvpRestoreStrategy struct {
381-
AllowNonRestoredState bool `json:"allowNonRestoredState,omitempty"`
381+
AllowNonRestoredState bool `json:"allowNonRestoredState"`
382382
Kind string `json:"kind,omitempty"`
383383
}
384384

api/v1alpha1/computeworkspace_types.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ type ComputeWorkspaceSpec struct {
3838

3939
// UseExternalAccess is the flag to indicate whether the workspace will use external access.
4040
// +optional
41-
UseExternalAccess *bool `json:"useExternalAccess,omitempty"`
41+
UseExternalAccess *bool `json:"useExternalAccess"`
4242

4343
// FlinkBlobStorage is the configuration for the Flink blob storage.
4444
// +optional

api/v1alpha1/pulsarconnection_types.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -78,11 +78,11 @@ type PulsarConnectionSpec struct {
7878
// TLSEnableHostnameVerification indicates whether to verify the hostname of the broker.
7979
// Only used when using secure urls.
8080
// +optional
81-
TLSEnableHostnameVerification bool `json:"tlsEnableHostnameVerification,omitempty"`
81+
TLSEnableHostnameVerification bool `json:"tlsEnableHostnameVerification"`
8282

8383
// TLSAllowInsecureConnection indicates whether to allow insecure connection to the broker.
8484
// +optional
85-
TLSAllowInsecureConnection bool `json:"tlsAllowInsecureConnection,omitempty"`
85+
TLSAllowInsecureConnection bool `json:"tlsAllowInsecureConnection"`
8686

8787
// TLSTrustCertsFilePath Path for the TLS certificate used to validate the broker endpoint when using TLS.
8888
// +optional

api/v1alpha1/pulsarfunction_types.go

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -38,27 +38,27 @@ type PulsarFunctionSpec struct {
3838

3939
// CleanupSubscription is the flag to indicate whether the subscription should be cleaned up when the function is deleted
4040
// +optional
41-
CleanupSubscription *bool `json:"cleanupSubscription,omitempty"`
41+
CleanupSubscription *bool `json:"cleanupSubscription"`
4242

4343
// RetainOrdering is the flag to indicate whether the function should retain ordering
4444
// +optional
45-
RetainOrdering *bool `json:"retainOrdering,omitempty"`
45+
RetainOrdering *bool `json:"retainOrdering"`
4646

4747
// RetainKeyOrdering is the flag to indicate whether the function should retain key ordering
4848
// +optional
49-
RetainKeyOrdering *bool `json:"retainKeyOrdering,omitempty"`
49+
RetainKeyOrdering *bool `json:"retainKeyOrdering"`
5050

5151
// BatchBuilder is the batch builder that the function uses
5252
// +optional
5353
BatchBuilder *string `json:"batchBuilder,omitempty"`
5454

5555
// ForwardSourceMessageProperty is the flag to indicate whether the function should forward source message properties
5656
// +optional
57-
ForwardSourceMessageProperty *bool `json:"forwardSourceMessageProperty,omitempty"`
57+
ForwardSourceMessageProperty *bool `json:"forwardSourceMessageProperty"`
5858

5959
// AutoAck is the flag to indicate whether the function should auto ack
6060
// +optional
61-
AutoAck *bool `json:"autoAck,omitempty"`
61+
AutoAck *bool `json:"autoAck"`
6262

6363
// Parallelism is the parallelism of the function
6464
// +optional
@@ -186,11 +186,11 @@ type PulsarFunctionSpec struct {
186186

187187
// ExposePulsarAdminClientEnabled is the flag to indicate whether the function should expose pulsar admin client
188188
// +optional
189-
ExposePulsarAdminClientEnabled *bool `json:"exposePulsarAdminClientEnabled,omitempty"`
189+
ExposePulsarAdminClientEnabled *bool `json:"exposePulsarAdminClientEnabled"`
190190

191191
// SkipToLatest is the flag to indicate whether the function should skip to latest
192192
// +optional
193-
SkipToLatest *bool `json:"skipToLatest,omitempty"`
193+
SkipToLatest *bool `json:"skipToLatest"`
194194

195195
// SubscriptionPosition is the subscription position of the function
196196
// +optional

api/v1alpha1/pulsarnamespace_types.go

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ import (
2626
// TopicAutoCreationConfig defines the configuration for automatic topic creation
2727
type TopicAutoCreationConfig struct {
2828
// Allow specifies whether to allow automatic topic creation
29-
Allow bool `json:"allow,omitempty"`
29+
Allow bool `json:"allow"`
3030

3131
// Type specifies the type of automatically created topics
3232
// +kubebuilder:validation:Enum=partitioned;non-partitioned
@@ -125,7 +125,7 @@ type InactiveTopicPolicies struct {
125125

126126
// DeleteWhileInactive specifies whether to delete topics while they are inactive
127127
// +optional
128-
DeleteWhileInactive *bool `json:"deleteWhileInactive,omitempty"`
128+
DeleteWhileInactive *bool `json:"deleteWhileInactive"`
129129
}
130130

131131
// EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN!
@@ -167,7 +167,7 @@ type PulsarNamespaceSpec struct {
167167
// When enabled, producers must provide a schema when publishing messages.
168168
// If not specified, the cluster's default schema validation enforcement setting will be used.
169169
// +optional
170-
SchemaValidationEnforced *bool `json:"schemaValidationEnforced,omitempty"`
170+
SchemaValidationEnforced *bool `json:"schemaValidationEnforced"`
171171

172172
// MaxProducersPerTopic sets the maximum number of producers allowed on a single topic in the namespace.
173173
// +optional
@@ -246,7 +246,7 @@ type PulsarNamespaceSpec struct {
246246

247247
// Deduplication controls whether to enable message deduplication for the namespace.
248248
// +optional
249-
Deduplication *bool `json:"deduplication,omitempty"`
249+
Deduplication *bool `json:"deduplication"`
250250

251251
// BookieAffinityGroup is the name of the namespace isolation policy to apply to the namespace.
252252
BookieAffinityGroup *BookieAffinityGroupData `json:"bookieAffinityGroup,omitempty"`
@@ -309,17 +309,17 @@ type PulsarNamespaceSpec struct {
309309
// IsAllowAutoUpdateSchema specifies whether to allow automatic schema updates.
310310
// When enabled, producers can automatically update schemas without manual approval.
311311
// +optional
312-
IsAllowAutoUpdateSchema *bool `json:"isAllowAutoUpdateSchema,omitempty"`
312+
IsAllowAutoUpdateSchema *bool `json:"isAllowAutoUpdateSchema"`
313313

314314
// ValidateProducerName specifies whether to validate producer names.
315315
// When enabled, producer names must follow specific naming conventions.
316316
// +optional
317-
ValidateProducerName *bool `json:"validateProducerName,omitempty"`
317+
ValidateProducerName *bool `json:"validateProducerName"`
318318

319319
// EncryptionRequired specifies whether message encryption is required for this namespace.
320320
// When enabled, all messages published to topics in this namespace must be encrypted.
321321
// +optional
322-
EncryptionRequired *bool `json:"encryptionRequired,omitempty"`
322+
EncryptionRequired *bool `json:"encryptionRequired"`
323323

324324
// SubscriptionAuthMode specifies the subscription authentication mode for this namespace.
325325
// Valid values are "None" and "Prefix".
@@ -370,7 +370,7 @@ type PulsarNamespaceStatus struct {
370370
// GeoReplicationEnabled indicates whether geo-replication between two Pulsar instances (via PulsarGeoReplication)
371371
// is enabled for the namespace
372372
// +optional
373-
GeoReplicationEnabled bool `json:"geoReplicationEnabled,omitempty"`
373+
GeoReplicationEnabled bool `json:"geoReplicationEnabled"`
374374
}
375375

376376
//+kubebuilder:object:root=true

api/v1alpha1/pulsarsink_types.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -42,19 +42,19 @@ type PulsarSinkSpec struct {
4242

4343
// CleanupSubscription is the flag to enable or disable the cleanup of subscription
4444
// +optional
45-
CleanupSubscription *bool `json:"cleanupSubscription,omitempty"`
45+
CleanupSubscription *bool `json:"cleanupSubscription"`
4646

4747
// RetainOrdering is the flag to enable or disable the retain ordering
4848
// +optional
49-
RetainOrdering *bool `json:"retainOrdering,omitempty"`
49+
RetainOrdering *bool `json:"retainOrdering"`
5050

5151
// RetainKeyOrdering is the flag to enable or disable the retain key ordering
5252
// +optional
53-
RetainKeyOrdering *bool `json:"retainKeyOrdering,omitempty"`
53+
RetainKeyOrdering *bool `json:"retainKeyOrdering"`
5454

5555
// AutoAck is the flag to enable or disable the auto ack
5656
// +optional
57-
AutoAck *bool `json:"autoAck,omitempty"`
57+
AutoAck *bool `json:"autoAck"`
5858

5959
// Parallelism is the parallelism of the PulsarSink
6060
// +optional

api/v1alpha1/pulsartopic_types.go

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ type PulsarTopicSpec struct {
4040
// Defaults to true if not specified.
4141
// +kubebuilder:default=true
4242
// +optional
43-
Persistent *bool `json:"persistent,omitempty"`
43+
Persistent *bool `json:"persistent"`
4444

4545
// Partitions specifies the number of partitions for a partitioned topic.
4646
// Set to 0 for a non-partitioned topic.
@@ -141,7 +141,7 @@ type PulsarTopicSpec struct {
141141

142142
// Deduplication controls whether to enable message deduplication for the topic.
143143
// +optional
144-
Deduplication *bool `json:"deduplication,omitempty"`
144+
Deduplication *bool `json:"deduplication"`
145145

146146
// CompactionThreshold specifies the size threshold in bytes for automatic topic compaction.
147147
// When the topic reaches this size, compaction will be triggered automatically.
@@ -194,7 +194,7 @@ type PulsarTopicSpec struct {
194194
// SchemaValidationEnforced determines whether schema validation is enforced for the topic.
195195
// When enabled, only messages that conform to the topic's schema will be accepted.
196196
// +optional
197-
SchemaValidationEnforced *bool `json:"schemaValidationEnforced,omitempty"`
197+
SchemaValidationEnforced *bool `json:"schemaValidationEnforced"`
198198

199199
// SubscriptionDispatchRate defines the message dispatch rate limiting policy for subscriptions.
200200
// This controls the rate at which messages are delivered to consumers per subscription.
@@ -237,7 +237,7 @@ type PulsarTopicSpec struct {
237237
type DelayedDeliveryData struct {
238238
// Active determines whether delayed delivery is enabled for the topic
239239
// +optional
240-
Active *bool `json:"active,omitempty"`
240+
Active *bool `json:"active"`
241241

242242
// TickTimeMillis specifies the tick time for delayed message delivery in milliseconds
243243
// +optional
@@ -288,7 +288,7 @@ type OffloadPolicies struct {
288288
// This is a local type definition that mirrors the external library's AutoSubscriptionCreationOverride
289289
// to ensure proper Kubernetes deep copy generation.
290290
type AutoSubscriptionCreationOverride struct {
291-
AllowAutoSubscriptionCreation bool `json:"allowAutoSubscriptionCreation,omitempty"`
291+
AllowAutoSubscriptionCreation bool `json:"allowAutoSubscriptionCreation"`
292292
}
293293

294294
// SchemaCompatibilityStrategy defines the schema compatibility strategy for a topic.
@@ -321,7 +321,7 @@ type PulsarTopicStatus struct {
321321
// GeoReplicationEnabled indicates whether geo-replication is enabled for this topic.
322322
// This is set to true when GeoReplicationRefs are configured in the spec and successfully applied.
323323
// +optional
324-
GeoReplicationEnabled bool `json:"geoReplicationEnabled,omitempty"`
324+
GeoReplicationEnabled bool `json:"geoReplicationEnabled"`
325325
}
326326

327327
//+kubebuilder:object:root=true
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
// Copyright 2026 StreamNative
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package controllers
16+
17+
import (
18+
"context"
19+
"encoding/json"
20+
21+
. "github.com/onsi/ginkgo"
22+
. "github.com/onsi/gomega"
23+
corev1 "k8s.io/api/core/v1"
24+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
25+
"k8s.io/apimachinery/pkg/types"
26+
27+
resourcev1alpha1 "github.com/streamnative/pulsar-resources-operator/api/v1alpha1"
28+
)
29+
30+
var _ = Describe("PulsarTopic Boolean Field Persistence", func() {
31+
Context("when PulsarTopic status has GeoReplicationEnabled set to false", func() {
32+
It("should preserve false value in JSON serialization", func() {
33+
ctx := context.Background()
34+
namespace := "default"
35+
topicName := "test-geo-replication-false"
36+
connectionName := "test-connection"
37+
38+
connection := &resourcev1alpha1.PulsarConnection{
39+
ObjectMeta: metav1.ObjectMeta{
40+
Name: connectionName,
41+
Namespace: namespace,
42+
},
43+
Spec: resourcev1alpha1.PulsarConnectionSpec{
44+
AdminServiceURL: "http://localhost:8080",
45+
BrokerServiceURL: "pulsar://localhost:6650",
46+
},
47+
}
48+
Expect(k8sClient.Create(ctx, connection)).Should(Succeed())
49+
50+
topic := &resourcev1alpha1.PulsarTopic{
51+
ObjectMeta: metav1.ObjectMeta{
52+
Name: topicName,
53+
Namespace: namespace,
54+
},
55+
Spec: resourcev1alpha1.PulsarTopicSpec{
56+
Name: "persistent://public/default/" + topicName,
57+
ConnectionRef: corev1.LocalObjectReference{
58+
Name: connectionName,
59+
},
60+
},
61+
Status: resourcev1alpha1.PulsarTopicStatus{
62+
GeoReplicationEnabled: false,
63+
},
64+
}
65+
Expect(k8sClient.Create(ctx, topic)).Should(Succeed())
66+
67+
// Get the created topic and verify status
68+
createdTopic := &resourcev1alpha1.PulsarTopic{}
69+
topicKey := types.NamespacedName{Name: topicName, Namespace: namespace}
70+
Expect(k8sClient.Get(ctx, topicKey, createdTopic)).Should(Succeed())
71+
Expect(createdTopic.Status.GeoReplicationEnabled).Should(Equal(false))
72+
73+
// Verify the serialization
74+
statusJSON, err := json.Marshal(createdTopic.Status)
75+
Expect(err).Should(Succeed())
76+
var statusMap map[string]interface{}
77+
Expect(json.Unmarshal(statusJSON, &statusMap)).Should(Succeed())
78+
Expect(statusMap).Should(HaveKey("geoReplicationEnabled"))
79+
geoRepValue, exists := statusMap["geoReplicationEnabled"]
80+
Expect(exists).Should(BeTrue())
81+
Expect(geoRepValue).Should(Equal(false))
82+
})
83+
})
84+
})

0 commit comments

Comments
 (0)