• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

nats-io / nats.java / #2109

15 Aug 2025 04:51PM UTC coverage: 95.404% (-0.05%) from 95.457%
#2109

push

github

web-flow
Merge pull request #1395 from nats-io/header-nullability

Header nullability

3 of 3 new or added lines in 1 file covered. (100.0%)

14 existing lines in 7 files now uncovered.

11915 of 12489 relevant lines covered (95.4%)

0.95 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

93.88
/src/main/java/io/nats/client/support/NatsKeyValueUtil.java
1
// Copyright 2021 The NATS Authors
2
// Licensed under the Apache License, Version 2.0 (the "License");
3
// you may not use this file except in compliance with the License.
4
// You may obtain a copy of the License at:
5
//
6
// http://www.apache.org/licenses/LICENSE-2.0
7
//
8
// Unless required by applicable law or agreed to in writing, software
9
// distributed under the License is distributed on an "AS IS" BASIS,
10
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11
// See the License for the specific language governing permissions and
12
// limitations under the License.
13

14
package io.nats.client.support;
15

16
import io.nats.client.Message;
17
import io.nats.client.MessageTtl;
18
import io.nats.client.PublishOptions;
19
import io.nats.client.api.KeyValueOperation;
20
import io.nats.client.impl.Headers;
21
import org.jspecify.annotations.NonNull;
22
import org.jspecify.annotations.Nullable;
23

24
import static io.nats.client.support.NatsConstants.DOT;
25
import static io.nats.client.support.NatsJetStreamConstants.*;
26

27
public abstract class NatsKeyValueUtil {
28

29
    private NatsKeyValueUtil() {} /* ensures cannot be constructed */
30

31
    public static final String KV_STREAM_PREFIX = "KV_";
32
    public static final int KV_STREAM_PREFIX_LEN = KV_STREAM_PREFIX.length();
1✔
33
    public static final String KV_SUBJECT_PREFIX = "$KV.";
34
    public static final String KV_SUBJECT_SUFFIX = ".>";
35
    public static final String KV_OPERATION_HEADER_KEY = "KV-Operation";
36

37
    @NonNull
38
    public static String extractBucketName(String streamName) {
39
        return streamName.substring(KV_STREAM_PREFIX_LEN);
1✔
40
    }
41

42
    @NonNull
43
    public static String toStreamName(String bucketName) {
44
        return KV_STREAM_PREFIX + bucketName;
1✔
45
    }
46

47
    @NonNull
48
    public static String toStreamSubject(String bucketName) {
49
        return KV_SUBJECT_PREFIX + bucketName + KV_SUBJECT_SUFFIX;
1✔
50
    }
51

52
    @NonNull
53
    public static String toKeyPrefix(String bucketName) {
54
        return KV_SUBJECT_PREFIX + bucketName + DOT;
1✔
55
    }
56

57
    public static boolean hasPrefix(String bucketName) {
58
        return bucketName.startsWith(KV_STREAM_PREFIX);
1✔
59
    }
60

61
    @NonNull
62
    public static String trimPrefix(String bucketName) {
63
        if (bucketName.startsWith(KV_STREAM_PREFIX)) {
1✔
64
            return bucketName.substring(KV_STREAM_PREFIX.length());
1✔
65
        }
66
        return bucketName;
1✔
67
    }
68

69
    @Nullable
70
    public static String getOperationHeader(Headers h) {
71
        return h == null ? null : h.getFirst(KV_OPERATION_HEADER_KEY);
1✔
72
    }
73

74
    @Nullable
75
    public static String getNatsMarkerReasonHeader(Headers h) {
76
        return h == null ? null : h.getFirst(NATS_MARKER_REASON_HDR);
1✔
77
    }
78

79
    @NonNull
80
    public static KeyValueOperation getOperation(Headers h) {
81
        KeyValueOperation kvo = null;
1✔
82
        String hs = getOperationHeader(h);
1✔
83
        if (hs != null) {
1✔
84
            kvo = KeyValueOperation.instance(hs);
1✔
85
        }
86
        if (kvo == null) {
1✔
87
            hs = getNatsMarkerReasonHeader(h);
1✔
88
            if (hs != null) {
1✔
UNCOV
89
                kvo = KeyValueOperation.instanceByMarkerReason(hs);
×
90
            }
91
        }
92
        return kvo == null ? KeyValueOperation.PUT : kvo;
1✔
93
    }
94

95
    @NonNull
96
    public static Headers getDeleteHeaders() {
97
        return new Headers()
1✔
98
            .put(KV_OPERATION_HEADER_KEY, KeyValueOperation.DELETE.getHeaderValue());
1✔
99
    }
100

101
    @NonNull
102
    public static Headers getPurgeHeaders() {
103
        return new Headers()
1✔
104
            .put(KV_OPERATION_HEADER_KEY, KeyValueOperation.PURGE.getHeaderValue())
1✔
105
            .put(ROLLUP_HDR, ROLLUP_HDR_SUBJECT);
1✔
106
    }
107

108
    @Nullable
109
    public static PublishOptions getPublishOptions(long expectedRevision, MessageTtl messageTtl) {
110
        boolean returnNull = true;
1✔
111
        PublishOptions.Builder b = PublishOptions.builder();
1✔
112
        if (expectedRevision > -1) {
1✔
113
            returnNull = false;
1✔
114
            b.expectedLastSubjectSequence(expectedRevision);
1✔
115
        }
116
        if (messageTtl != null) {
1✔
UNCOV
117
            returnNull = false;
×
UNCOV
118
            b.messageTtl(messageTtl);
×
119
        }
120
        return returnNull ? null : b.build();
1✔
121
    }
122

123
    public static class BucketAndKey {
124
        public final String bucket;
125
        public final String key;
126

127
        public BucketAndKey(Message m) {
128
            this(m.getSubject());
1✔
129
        }
1✔
130

131
        public BucketAndKey(String subject) {
1✔
132
            String[] split = subject.split("\\Q.\\E", 3);
1✔
133
            bucket = split[1];
1✔
134
            key = split[2];
1✔
135
        }
1✔
136

137
        @Override
138
        public boolean equals(Object o) {
139
            if (this == o) return true;
1✔
140
            if (o == null || getClass() != o.getClass()) return false;
1✔
141

142
            BucketAndKey that = (BucketAndKey) o;
1✔
143

144
            if (!bucket.equals(that.bucket)) return false;
1✔
145
            return key.equals(that.key);
1✔
146
        }
147

148
        @Override
149
        public int hashCode() {
150
            int result = bucket.hashCode();
1✔
151
            result = 31 * result + key.hashCode();
1✔
152
            return result;
1✔
153
        }
154
    }
155
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc