Showing
25 changed files
with
310 additions
and
89 deletions
application/src/main/java/org/thingsboard/server/service/apiusage/DefaultTbApiUsageStateService.java
@@ -17,6 +17,7 @@ package org.thingsboard.server.service.apiusage; | @@ -17,6 +17,7 @@ package org.thingsboard.server.service.apiusage; | ||
17 | 17 | ||
18 | import lombok.extern.slf4j.Slf4j; | 18 | import lombok.extern.slf4j.Slf4j; |
19 | import org.springframework.beans.factory.annotation.Value; | 19 | import org.springframework.beans.factory.annotation.Value; |
20 | +import org.springframework.data.util.Pair; | ||
20 | import org.springframework.stereotype.Service; | 21 | import org.springframework.stereotype.Service; |
21 | import org.thingsboard.server.common.data.ApiUsageRecordKey; | 22 | import org.thingsboard.server.common.data.ApiUsageRecordKey; |
22 | import org.thingsboard.server.common.data.ApiUsageState; | 23 | import org.thingsboard.server.common.data.ApiUsageState; |
@@ -33,15 +34,18 @@ import org.thingsboard.server.dao.timeseries.TimeseriesService; | @@ -33,15 +34,18 @@ import org.thingsboard.server.dao.timeseries.TimeseriesService; | ||
33 | import org.thingsboard.server.dao.usagerecord.ApiUsageStateService; | 34 | import org.thingsboard.server.dao.usagerecord.ApiUsageStateService; |
34 | import org.thingsboard.server.gen.transport.TransportProtos.ToUsageStatsServiceMsg; | 35 | import org.thingsboard.server.gen.transport.TransportProtos.ToUsageStatsServiceMsg; |
35 | import org.thingsboard.server.gen.transport.TransportProtos.UsageStatsKVProto; | 36 | import org.thingsboard.server.gen.transport.TransportProtos.UsageStatsKVProto; |
37 | +import org.thingsboard.server.queue.TbQueueCallback; | ||
36 | import org.thingsboard.server.queue.common.TbProtoQueueMsg; | 38 | import org.thingsboard.server.queue.common.TbProtoQueueMsg; |
37 | import org.thingsboard.server.queue.discovery.PartitionChangeEvent; | 39 | import org.thingsboard.server.queue.discovery.PartitionChangeEvent; |
38 | import org.thingsboard.server.queue.discovery.PartitionService; | 40 | import org.thingsboard.server.queue.discovery.PartitionService; |
39 | import org.thingsboard.server.queue.scheduler.SchedulerComponent; | 41 | import org.thingsboard.server.queue.scheduler.SchedulerComponent; |
40 | import org.thingsboard.server.queue.util.TbCoreComponent; | 42 | import org.thingsboard.server.queue.util.TbCoreComponent; |
41 | import org.thingsboard.server.service.profile.TbTenantProfileCache; | 43 | import org.thingsboard.server.service.profile.TbTenantProfileCache; |
44 | +import org.thingsboard.server.service.queue.TbClusterService; | ||
42 | 45 | ||
43 | import javax.annotation.PostConstruct; | 46 | import javax.annotation.PostConstruct; |
44 | import java.util.ArrayList; | 47 | import java.util.ArrayList; |
48 | +import java.util.HashMap; | ||
45 | import java.util.List; | 49 | import java.util.List; |
46 | import java.util.Map; | 50 | import java.util.Map; |
47 | import java.util.UUID; | 51 | import java.util.UUID; |
@@ -57,12 +61,17 @@ import java.util.concurrent.locks.ReentrantLock; | @@ -57,12 +61,17 @@ import java.util.concurrent.locks.ReentrantLock; | ||
57 | public class DefaultTbApiUsageStateService implements TbApiUsageStateService { | 61 | public class DefaultTbApiUsageStateService implements TbApiUsageStateService { |
58 | 62 | ||
59 | public static final String HOURLY = "HOURLY_"; | 63 | public static final String HOURLY = "HOURLY_"; |
64 | + private final TbClusterService clusterService; | ||
60 | private final PartitionService partitionService; | 65 | private final PartitionService partitionService; |
61 | private final ApiUsageStateService apiUsageStateService; | 66 | private final ApiUsageStateService apiUsageStateService; |
62 | private final TimeseriesService tsService; | 67 | private final TimeseriesService tsService; |
63 | private final SchedulerComponent scheduler; | 68 | private final SchedulerComponent scheduler; |
64 | private final TbTenantProfileCache tenantProfileCache; | 69 | private final TbTenantProfileCache tenantProfileCache; |
65 | - private final Map<TenantId, TenantApiUsageState> tenantStates = new ConcurrentHashMap<>(); | 70 | + |
71 | + // Tenants that should be processed on this server | ||
72 | + private final Map<TenantId, TenantApiUsageState> myTenantStates = new ConcurrentHashMap<>(); | ||
73 | + // Tenants that should be processed on other servers | ||
74 | + private final Map<TenantId, ApiUsageState> otherTenantStates = new ConcurrentHashMap<>(); | ||
66 | 75 | ||
67 | @Value("${usage.stats.report.enabled:true}") | 76 | @Value("${usage.stats.report.enabled:true}") |
68 | private boolean enabled; | 77 | private boolean enabled; |
@@ -72,7 +81,13 @@ public class DefaultTbApiUsageStateService implements TbApiUsageStateService { | @@ -72,7 +81,13 @@ public class DefaultTbApiUsageStateService implements TbApiUsageStateService { | ||
72 | 81 | ||
73 | private final Lock updateLock = new ReentrantLock(); | 82 | private final Lock updateLock = new ReentrantLock(); |
74 | 83 | ||
75 | - public DefaultTbApiUsageStateService(PartitionService partitionService, ApiUsageStateService apiUsageStateService, TimeseriesService tsService, SchedulerComponent scheduler, TbTenantProfileCache tenantProfileCache) { | 84 | + public DefaultTbApiUsageStateService(TbClusterService clusterService, |
85 | + PartitionService partitionService, | ||
86 | + ApiUsageStateService apiUsageStateService, | ||
87 | + TimeseriesService tsService, | ||
88 | + SchedulerComponent scheduler, | ||
89 | + TbTenantProfileCache tenantProfileCache) { | ||
90 | + this.clusterService = clusterService; | ||
76 | this.partitionService = partitionService; | 91 | this.partitionService = partitionService; |
77 | this.apiUsageStateService = apiUsageStateService; | 92 | this.apiUsageStateService = apiUsageStateService; |
78 | this.tsService = tsService; | 93 | this.tsService = tsService; |
@@ -93,7 +108,7 @@ public class DefaultTbApiUsageStateService implements TbApiUsageStateService { | @@ -93,7 +108,7 @@ public class DefaultTbApiUsageStateService implements TbApiUsageStateService { | ||
93 | TenantId tenantId = new TenantId(new UUID(statsMsg.getTenantIdMSB(), statsMsg.getTenantIdLSB())); | 108 | TenantId tenantId = new TenantId(new UUID(statsMsg.getTenantIdMSB(), statsMsg.getTenantIdLSB())); |
94 | TenantApiUsageState tenantState; | 109 | TenantApiUsageState tenantState; |
95 | List<TsKvEntry> updatedEntries; | 110 | List<TsKvEntry> updatedEntries; |
96 | - boolean stateUpdated = false; | 111 | + Map<ApiFeature, Boolean> result = new HashMap<>(); |
97 | updateLock.lock(); | 112 | updateLock.lock(); |
98 | try { | 113 | try { |
99 | tenantState = getOrFetchState(tenantId); | 114 | tenantState = getOrFetchState(tenantId); |
@@ -110,32 +125,49 @@ public class DefaultTbApiUsageStateService implements TbApiUsageStateService { | @@ -110,32 +125,49 @@ public class DefaultTbApiUsageStateService implements TbApiUsageStateService { | ||
110 | updatedEntries.add(new BasicTsKvEntry(ts, new LongDataEntry(recordKey.name(), newValue))); | 125 | updatedEntries.add(new BasicTsKvEntry(ts, new LongDataEntry(recordKey.name(), newValue))); |
111 | long newHourlyValue = tenantState.addToHourly(recordKey, kvProto.getValue()); | 126 | long newHourlyValue = tenantState.addToHourly(recordKey, kvProto.getValue()); |
112 | updatedEntries.add(new BasicTsKvEntry(hourTs, new LongDataEntry(HOURLY + recordKey.name(), newHourlyValue))); | 127 | updatedEntries.add(new BasicTsKvEntry(hourTs, new LongDataEntry(HOURLY + recordKey.name(), newHourlyValue))); |
113 | - stateUpdated |= tenantState.checkStateUpdatedDueToThreshold(recordKey); | 128 | + Pair<ApiFeature, Boolean> update = tenantState.checkStateUpdatedDueToThreshold(recordKey); |
129 | + if (update != null) { | ||
130 | + result.put(update.getFirst(), update.getSecond()); | ||
131 | + } | ||
114 | } | 132 | } |
115 | } finally { | 133 | } finally { |
116 | updateLock.unlock(); | 134 | updateLock.unlock(); |
117 | } | 135 | } |
118 | tsService.save(tenantId, tenantState.getApiUsageState().getId(), updatedEntries, 0L); | 136 | tsService.save(tenantId, tenantState.getApiUsageState().getId(), updatedEntries, 0L); |
119 | - if (stateUpdated) { | ||
120 | - // Save new state into the database; | ||
121 | - apiUsageStateService.update(tenantState.getApiUsageState()); | ||
122 | - //TODO: clear cache on cluster repartition. | ||
123 | - //TODO: update profiles on tenant and profile updates. | ||
124 | - //TODO: broadcast to everyone notifications about enabled/disabled features. | 137 | + if (!result.isEmpty()) { |
138 | + persistAndNotify(tenantState, result); | ||
125 | } | 139 | } |
126 | } | 140 | } |
127 | 141 | ||
128 | @Override | 142 | @Override |
129 | public void onApplicationEvent(PartitionChangeEvent partitionChangeEvent) { | 143 | public void onApplicationEvent(PartitionChangeEvent partitionChangeEvent) { |
130 | if (partitionChangeEvent.getServiceType().equals(ServiceType.TB_CORE)) { | 144 | if (partitionChangeEvent.getServiceType().equals(ServiceType.TB_CORE)) { |
131 | - tenantStates.entrySet().removeIf(entry -> !partitionService.resolve(ServiceType.TB_CORE, entry.getKey(), entry.getKey()).isMyPartition()); | 145 | + myTenantStates.entrySet().removeIf(entry -> !partitionService.resolve(ServiceType.TB_CORE, entry.getKey(), entry.getKey()).isMyPartition()); |
146 | + otherTenantStates.entrySet().removeIf(entry -> partitionService.resolve(ServiceType.TB_CORE, entry.getKey(), entry.getKey()).isMyPartition()); | ||
132 | } | 147 | } |
133 | } | 148 | } |
134 | 149 | ||
135 | @Override | 150 | @Override |
136 | - public TenantApiUsageState getApiUsageState(TenantId tenantId) { | ||
137 | - //We should always get it from the map of from the database; | ||
138 | - return null; | 151 | + public ApiUsageState getApiUsageState(TenantId tenantId) { |
152 | + if (partitionService.resolve(ServiceType.TB_CORE, tenantId, tenantId).isMyPartition()) { | ||
153 | + TenantApiUsageState state = getOrFetchState(tenantId); | ||
154 | + return state.getApiUsageState(); | ||
155 | + } else { | ||
156 | + ApiUsageState state = otherTenantStates.get(tenantId); | ||
157 | + if (state == null) { | ||
158 | + updateLock.lock(); | ||
159 | + try { | ||
160 | + state = otherTenantStates.get(tenantId); | ||
161 | + if (state == null) { | ||
162 | + state = apiUsageStateService.findTenantApiUsageState(tenantId); | ||
163 | + otherTenantStates.put(tenantId, state); | ||
164 | + } | ||
165 | + } finally { | ||
166 | + updateLock.unlock(); | ||
167 | + } | ||
168 | + } | ||
169 | + return state; | ||
170 | + } | ||
139 | } | 171 | } |
140 | 172 | ||
141 | @Override | 173 | @Override |
@@ -143,7 +175,7 @@ public class DefaultTbApiUsageStateService implements TbApiUsageStateService { | @@ -143,7 +175,7 @@ public class DefaultTbApiUsageStateService implements TbApiUsageStateService { | ||
143 | TenantProfile tenantProfile = tenantProfileCache.get(tenantProfileId); | 175 | TenantProfile tenantProfile = tenantProfileCache.get(tenantProfileId); |
144 | updateLock.lock(); | 176 | updateLock.lock(); |
145 | try { | 177 | try { |
146 | - tenantStates.values().forEach(state -> { | 178 | + myTenantStates.values().forEach(state -> { |
147 | if (tenantProfile.getId().equals(state.getTenantProfileId())) { | 179 | if (tenantProfile.getId().equals(state.getTenantProfileId())) { |
148 | updateTenantState(state, tenantProfile); | 180 | updateTenantState(state, tenantProfile); |
149 | } | 181 | } |
@@ -158,7 +190,7 @@ public class DefaultTbApiUsageStateService implements TbApiUsageStateService { | @@ -158,7 +190,7 @@ public class DefaultTbApiUsageStateService implements TbApiUsageStateService { | ||
158 | TenantProfile tenantProfile = tenantProfileCache.get(tenantId); | 190 | TenantProfile tenantProfile = tenantProfileCache.get(tenantId); |
159 | updateLock.lock(); | 191 | updateLock.lock(); |
160 | try { | 192 | try { |
161 | - TenantApiUsageState state = tenantStates.get(tenantId); | 193 | + TenantApiUsageState state = myTenantStates.get(tenantId); |
162 | if (state != null && !state.getTenantProfileId().equals(tenantProfile.getId())) { | 194 | if (state != null && !state.getTenantProfileId().equals(tenantProfile.getId())) { |
163 | updateTenantState(state, tenantProfile); | 195 | updateTenantState(state, tenantProfile); |
164 | } | 196 | } |
@@ -167,12 +199,28 @@ public class DefaultTbApiUsageStateService implements TbApiUsageStateService { | @@ -167,12 +199,28 @@ public class DefaultTbApiUsageStateService implements TbApiUsageStateService { | ||
167 | } | 199 | } |
168 | } | 200 | } |
169 | 201 | ||
202 | + @Override | ||
203 | + public void onApiUsageStateUpdate(TenantId tenantId) { | ||
204 | + | ||
205 | + } | ||
170 | 206 | ||
171 | private void updateTenantState(TenantApiUsageState state, TenantProfile tenantProfile) { | 207 | private void updateTenantState(TenantApiUsageState state, TenantProfile tenantProfile) { |
172 | state.setTenantProfileData(tenantProfile.getProfileData()); | 208 | state.setTenantProfileData(tenantProfile.getProfileData()); |
173 | - if (state.checkStateUpdatedDueToThresholds()) { | ||
174 | - apiUsageStateService.update(state.getApiUsageState()); | ||
175 | - //TODO: send notification to cluster; | 209 | + Map<ApiFeature, Boolean> result = state.checkStateUpdatedDueToThresholds(); |
210 | + if (!result.isEmpty()) { | ||
211 | + persistAndNotify(state, result); | ||
212 | + } | ||
213 | + } | ||
214 | + | ||
215 | + private void persistAndNotify(TenantApiUsageState state, Map<ApiFeature, Boolean> result) { | ||
216 | + // TODO: | ||
217 | + // 1. Broadcast to everyone notifications about enabled/disabled features. | ||
218 | + // 2. Report rule engine and js executor metrics | ||
219 | + // 4. UI for configuration of the thresholds | ||
220 | + // 5. Max rule node executions per message. | ||
221 | + apiUsageStateService.update(state.getApiUsageState()); | ||
222 | + if (result.containsKey(ApiFeature.TRANSPORT)) { | ||
223 | + clusterService.onApiStateChange(state.getApiUsageState(), null); | ||
176 | } | 224 | } |
177 | } | 225 | } |
178 | 226 | ||
@@ -180,7 +228,7 @@ public class DefaultTbApiUsageStateService implements TbApiUsageStateService { | @@ -180,7 +228,7 @@ public class DefaultTbApiUsageStateService implements TbApiUsageStateService { | ||
180 | updateLock.lock(); | 228 | updateLock.lock(); |
181 | try { | 229 | try { |
182 | long now = System.currentTimeMillis(); | 230 | long now = System.currentTimeMillis(); |
183 | - tenantStates.values().forEach(state -> { | 231 | + myTenantStates.values().forEach(state -> { |
184 | if ((state.getNextCycleTs() > now) && (state.getNextCycleTs() - now < TimeUnit.HOURS.toMillis(1))) { | 232 | if ((state.getNextCycleTs() > now) && (state.getNextCycleTs() - now < TimeUnit.HOURS.toMillis(1))) { |
185 | state.setCycles(state.getNextCycleTs(), SchedulerUtils.getStartOfNextNextMonth()); | 233 | state.setCycles(state.getNextCycleTs(), SchedulerUtils.getStartOfNextNextMonth()); |
186 | } | 234 | } |
@@ -191,7 +239,7 @@ public class DefaultTbApiUsageStateService implements TbApiUsageStateService { | @@ -191,7 +239,7 @@ public class DefaultTbApiUsageStateService implements TbApiUsageStateService { | ||
191 | } | 239 | } |
192 | 240 | ||
193 | private TenantApiUsageState getOrFetchState(TenantId tenantId) { | 241 | private TenantApiUsageState getOrFetchState(TenantId tenantId) { |
194 | - TenantApiUsageState tenantState = tenantStates.get(tenantId); | 242 | + TenantApiUsageState tenantState = myTenantStates.get(tenantId); |
195 | if (tenantState == null) { | 243 | if (tenantState == null) { |
196 | ApiUsageState dbStateEntity = apiUsageStateService.findTenantApiUsageState(tenantId); | 244 | ApiUsageState dbStateEntity = apiUsageStateService.findTenantApiUsageState(tenantId); |
197 | if (dbStateEntity == null) { | 245 | if (dbStateEntity == null) { |
@@ -223,7 +271,7 @@ public class DefaultTbApiUsageStateService implements TbApiUsageStateService { | @@ -223,7 +271,7 @@ public class DefaultTbApiUsageStateService implements TbApiUsageStateService { | ||
223 | } | 271 | } |
224 | } | 272 | } |
225 | } | 273 | } |
226 | - tenantStates.put(tenantId, tenantState); | 274 | + myTenantStates.put(tenantId, tenantState); |
227 | } catch (InterruptedException | ExecutionException e) { | 275 | } catch (InterruptedException | ExecutionException e) { |
228 | log.warn("[{}] Failed to fetch api usage state from db.", tenantId, e); | 276 | log.warn("[{}] Failed to fetch api usage state from db.", tenantId, e); |
229 | } | 277 | } |
@@ -5,7 +5,7 @@ | @@ -5,7 +5,7 @@ | ||
5 | * you may not use this file except in compliance with the License. | 5 | * you may not use this file except in compliance with the License. |
6 | * You may obtain a copy of the License at | 6 | * You may obtain a copy of the License at |
7 | * | 7 | * |
8 | - * http://www.apache.org/licenses/LICENSE-2.0 | 8 | + * http://www.apache.org/licenses/LICENSE-2.0 |
9 | * | 9 | * |
10 | * Unless required by applicable law or agreed to in writing, software | 10 | * Unless required by applicable law or agreed to in writing, software |
11 | * distributed under the License is distributed on an "AS IS" BASIS, | 11 | * distributed under the License is distributed on an "AS IS" BASIS, |
@@ -16,6 +16,7 @@ | @@ -16,6 +16,7 @@ | ||
16 | package org.thingsboard.server.service.apiusage; | 16 | package org.thingsboard.server.service.apiusage; |
17 | 17 | ||
18 | import org.springframework.context.ApplicationListener; | 18 | import org.springframework.context.ApplicationListener; |
19 | +import org.thingsboard.server.common.data.ApiUsageState; | ||
19 | import org.thingsboard.server.common.data.id.TenantId; | 20 | import org.thingsboard.server.common.data.id.TenantId; |
20 | import org.thingsboard.server.common.data.id.TenantProfileId; | 21 | import org.thingsboard.server.common.data.id.TenantProfileId; |
21 | import org.thingsboard.server.common.msg.queue.TbCallback; | 22 | import org.thingsboard.server.common.msg.queue.TbCallback; |
@@ -27,9 +28,11 @@ public interface TbApiUsageStateService extends ApplicationListener<PartitionCha | @@ -27,9 +28,11 @@ public interface TbApiUsageStateService extends ApplicationListener<PartitionCha | ||
27 | 28 | ||
28 | void process(TbProtoQueueMsg<ToUsageStatsServiceMsg> msg, TbCallback callback); | 29 | void process(TbProtoQueueMsg<ToUsageStatsServiceMsg> msg, TbCallback callback); |
29 | 30 | ||
30 | - TenantApiUsageState getApiUsageState(TenantId tenantId); | 31 | + ApiUsageState getApiUsageState(TenantId tenantId); |
31 | 32 | ||
32 | void onTenantProfileUpdate(TenantProfileId tenantProfileId); | 33 | void onTenantProfileUpdate(TenantProfileId tenantProfileId); |
33 | 34 | ||
34 | void onTenantUpdate(TenantId tenantId); | 35 | void onTenantUpdate(TenantId tenantId); |
36 | + | ||
37 | + void onApiUsageStateUpdate(TenantId tenantId); | ||
35 | } | 38 | } |
@@ -5,7 +5,7 @@ | @@ -5,7 +5,7 @@ | ||
5 | * you may not use this file except in compliance with the License. | 5 | * you may not use this file except in compliance with the License. |
6 | * You may obtain a copy of the License at | 6 | * You may obtain a copy of the License at |
7 | * | 7 | * |
8 | - * http://www.apache.org/licenses/LICENSE-2.0 | 8 | + * http://www.apache.org/licenses/LICENSE-2.0 |
9 | * | 9 | * |
10 | * Unless required by applicable law or agreed to in writing, software | 10 | * Unless required by applicable law or agreed to in writing, software |
11 | * distributed under the License is distributed on an "AS IS" BASIS, | 11 | * distributed under the License is distributed on an "AS IS" BASIS, |
@@ -17,14 +17,17 @@ package org.thingsboard.server.service.apiusage; | @@ -17,14 +17,17 @@ package org.thingsboard.server.service.apiusage; | ||
17 | 17 | ||
18 | import lombok.Getter; | 18 | import lombok.Getter; |
19 | import lombok.Setter; | 19 | import lombok.Setter; |
20 | +import org.springframework.data.util.Pair; | ||
20 | import org.thingsboard.server.common.data.ApiUsageRecordKey; | 21 | import org.thingsboard.server.common.data.ApiUsageRecordKey; |
21 | import org.thingsboard.server.common.data.ApiUsageState; | 22 | import org.thingsboard.server.common.data.ApiUsageState; |
22 | import org.thingsboard.server.common.data.TenantProfile; | 23 | import org.thingsboard.server.common.data.TenantProfile; |
23 | import org.thingsboard.server.common.data.TenantProfileData; | 24 | import org.thingsboard.server.common.data.TenantProfileData; |
24 | import org.thingsboard.server.common.data.id.EntityId; | 25 | import org.thingsboard.server.common.data.id.EntityId; |
26 | +import org.thingsboard.server.common.data.id.TenantId; | ||
25 | import org.thingsboard.server.common.data.id.TenantProfileId; | 27 | import org.thingsboard.server.common.data.id.TenantProfileId; |
26 | import org.thingsboard.server.common.msg.tools.SchedulerUtils; | 28 | import org.thingsboard.server.common.msg.tools.SchedulerUtils; |
27 | 29 | ||
30 | +import java.util.HashMap; | ||
28 | import java.util.Map; | 31 | import java.util.Map; |
29 | import java.util.concurrent.ConcurrentHashMap; | 32 | import java.util.concurrent.ConcurrentHashMap; |
30 | 33 | ||
@@ -103,11 +106,17 @@ public class TenantApiUsageState { | @@ -103,11 +106,17 @@ public class TenantApiUsageState { | ||
103 | return Long.parseLong((String) threshold); | 106 | return Long.parseLong((String) threshold); |
104 | } else if (threshold instanceof Long) { | 107 | } else if (threshold instanceof Long) { |
105 | return (Long) threshold; | 108 | return (Long) threshold; |
109 | + } else if (threshold instanceof Integer) { | ||
110 | + return (Integer) threshold; | ||
106 | } | 111 | } |
107 | } | 112 | } |
108 | return 0L; | 113 | return 0L; |
109 | } | 114 | } |
110 | 115 | ||
116 | + public TenantId getTenantId() { | ||
117 | + return apiUsageState.getTenantId(); | ||
118 | + } | ||
119 | + | ||
111 | public EntityId getEntityId() { | 120 | public EntityId getEntityId() { |
112 | return apiUsageState.getEntityId(); | 121 | return apiUsageState.getEntityId(); |
113 | } | 122 | } |
@@ -121,7 +130,7 @@ public class TenantApiUsageState { | @@ -121,7 +130,7 @@ public class TenantApiUsageState { | ||
121 | } | 130 | } |
122 | 131 | ||
123 | public boolean isRuleEngineEnabled() { | 132 | public boolean isRuleEngineEnabled() { |
124 | - return apiUsageState.isRuleEngineEnabled(); | 133 | + return apiUsageState.isReExecEnabled(); |
125 | } | 134 | } |
126 | 135 | ||
127 | public boolean isJsExecEnabled() { | 136 | public boolean isJsExecEnabled() { |
@@ -137,7 +146,7 @@ public class TenantApiUsageState { | @@ -137,7 +146,7 @@ public class TenantApiUsageState { | ||
137 | } | 146 | } |
138 | 147 | ||
139 | public void setRuleEngineEnabled(boolean ruleEngineEnabled) { | 148 | public void setRuleEngineEnabled(boolean ruleEngineEnabled) { |
140 | - apiUsageState.setRuleEngineEnabled(ruleEngineEnabled); | 149 | + apiUsageState.setReExecEnabled(ruleEngineEnabled); |
141 | } | 150 | } |
142 | 151 | ||
143 | public void setJsExecEnabled(boolean jsExecEnabled) { | 152 | public void setJsExecEnabled(boolean jsExecEnabled) { |
@@ -147,7 +156,6 @@ public class TenantApiUsageState { | @@ -147,7 +156,6 @@ public class TenantApiUsageState { | ||
147 | public boolean isFeatureEnabled(ApiUsageRecordKey recordKey) { | 156 | public boolean isFeatureEnabled(ApiUsageRecordKey recordKey) { |
148 | switch (recordKey) { | 157 | switch (recordKey) { |
149 | case MSG_COUNT: | 158 | case MSG_COUNT: |
150 | - case MSG_BYTES_COUNT: | ||
151 | case DP_TRANSPORT_COUNT: | 159 | case DP_TRANSPORT_COUNT: |
152 | return isTransportEnabled(); | 160 | return isTransportEnabled(); |
153 | case RE_EXEC_COUNT: | 161 | case RE_EXEC_COUNT: |
@@ -161,38 +169,47 @@ public class TenantApiUsageState { | @@ -161,38 +169,47 @@ public class TenantApiUsageState { | ||
161 | } | 169 | } |
162 | } | 170 | } |
163 | 171 | ||
164 | - public boolean setFeatureValue(ApiUsageRecordKey recordKey, boolean value) { | 172 | + public ApiFeature setFeatureValue(ApiUsageRecordKey recordKey, boolean value) { |
173 | + ApiFeature feature = null; | ||
165 | boolean currentValue = isFeatureEnabled(recordKey); | 174 | boolean currentValue = isFeatureEnabled(recordKey); |
166 | switch (recordKey) { | 175 | switch (recordKey) { |
167 | case MSG_COUNT: | 176 | case MSG_COUNT: |
168 | - case MSG_BYTES_COUNT: | ||
169 | case DP_TRANSPORT_COUNT: | 177 | case DP_TRANSPORT_COUNT: |
178 | + feature = ApiFeature.TRANSPORT; | ||
170 | setTransportEnabled(value); | 179 | setTransportEnabled(value); |
171 | break; | 180 | break; |
172 | case RE_EXEC_COUNT: | 181 | case RE_EXEC_COUNT: |
182 | + feature = ApiFeature.RE; | ||
173 | setRuleEngineEnabled(value); | 183 | setRuleEngineEnabled(value); |
174 | break; | 184 | break; |
175 | case DP_STORAGE_COUNT: | 185 | case DP_STORAGE_COUNT: |
186 | + feature = ApiFeature.DB; | ||
176 | setDbStorageEnabled(value); | 187 | setDbStorageEnabled(value); |
177 | break; | 188 | break; |
178 | case JS_EXEC_COUNT: | 189 | case JS_EXEC_COUNT: |
190 | + feature = ApiFeature.JS; | ||
179 | setJsExecEnabled(value); | 191 | setJsExecEnabled(value); |
180 | break; | 192 | break; |
181 | } | 193 | } |
182 | - return currentValue == value; | 194 | + return currentValue == value ? null : feature; |
183 | } | 195 | } |
184 | 196 | ||
185 | - public boolean checkStateUpdatedDueToThresholds() { | ||
186 | - boolean update = false; | 197 | + public Map<ApiFeature, Boolean> checkStateUpdatedDueToThresholds() { |
198 | + Map<ApiFeature, Boolean> result = new HashMap<>(); | ||
187 | for (ApiUsageRecordKey key : ApiUsageRecordKey.values()) { | 199 | for (ApiUsageRecordKey key : ApiUsageRecordKey.values()) { |
188 | - update |= checkStateUpdatedDueToThreshold(key); | 200 | + Pair<ApiFeature, Boolean> featureUpdate = checkStateUpdatedDueToThreshold(key); |
201 | + if (featureUpdate != null) { | ||
202 | + result.put(featureUpdate.getFirst(), featureUpdate.getSecond()); | ||
203 | + } | ||
189 | } | 204 | } |
190 | - return update; | 205 | + return result; |
191 | } | 206 | } |
192 | 207 | ||
193 | - public boolean checkStateUpdatedDueToThreshold(ApiUsageRecordKey recordKey) { | 208 | + public Pair<ApiFeature, Boolean> checkStateUpdatedDueToThreshold(ApiUsageRecordKey recordKey) { |
194 | long value = get(recordKey); | 209 | long value = get(recordKey); |
195 | long threshold = getProfileThreshold(recordKey); | 210 | long threshold = getProfileThreshold(recordKey); |
196 | - return setFeatureValue(recordKey, threshold == 0 || value < threshold); | 211 | + boolean featureValue = threshold == 0 || value < threshold; |
212 | + ApiFeature feature = setFeatureValue(recordKey, featureValue); | ||
213 | + return feature != null ? Pair.of(feature, featureValue) : null; | ||
197 | } | 214 | } |
198 | } | 215 | } |
@@ -5,7 +5,7 @@ | @@ -5,7 +5,7 @@ | ||
5 | * you may not use this file except in compliance with the License. | 5 | * you may not use this file except in compliance with the License. |
6 | * You may obtain a copy of the License at | 6 | * You may obtain a copy of the License at |
7 | * | 7 | * |
8 | - * http://www.apache.org/licenses/LICENSE-2.0 | 8 | + * http://www.apache.org/licenses/LICENSE-2.0 |
9 | * | 9 | * |
10 | * Unless required by applicable law or agreed to in writing, software | 10 | * Unless required by applicable law or agreed to in writing, software |
11 | * distributed under the License is distributed on an "AS IS" BASIS, | 11 | * distributed under the License is distributed on an "AS IS" BASIS, |
@@ -21,6 +21,7 @@ import org.springframework.beans.factory.annotation.Value; | @@ -21,6 +21,7 @@ import org.springframework.beans.factory.annotation.Value; | ||
21 | import org.springframework.scheduling.annotation.Scheduled; | 21 | import org.springframework.scheduling.annotation.Scheduled; |
22 | import org.springframework.stereotype.Service; | 22 | import org.springframework.stereotype.Service; |
23 | import org.thingsboard.rule.engine.api.msg.ToDeviceActorNotificationMsg; | 23 | import org.thingsboard.rule.engine.api.msg.ToDeviceActorNotificationMsg; |
24 | +import org.thingsboard.server.common.data.ApiUsageState; | ||
24 | import org.thingsboard.server.common.data.DeviceProfile; | 25 | import org.thingsboard.server.common.data.DeviceProfile; |
25 | import org.thingsboard.server.common.data.EntityType; | 26 | import org.thingsboard.server.common.data.EntityType; |
26 | import org.thingsboard.server.common.data.HasName; | 27 | import org.thingsboard.server.common.data.HasName; |
@@ -47,6 +48,7 @@ import org.thingsboard.server.gen.transport.TransportProtos.ToRuleEngineNotifica | @@ -47,6 +48,7 @@ import org.thingsboard.server.gen.transport.TransportProtos.ToRuleEngineNotifica | ||
47 | import org.thingsboard.server.gen.transport.TransportProtos.ToTransportMsg; | 48 | import org.thingsboard.server.gen.transport.TransportProtos.ToTransportMsg; |
48 | import org.thingsboard.server.queue.TbQueueCallback; | 49 | import org.thingsboard.server.queue.TbQueueCallback; |
49 | import org.thingsboard.server.queue.TbQueueProducer; | 50 | import org.thingsboard.server.queue.TbQueueProducer; |
51 | +import org.thingsboard.server.queue.common.MultipleTbQueueCallbackWrapper; | ||
50 | import org.thingsboard.server.queue.common.TbProtoQueueMsg; | 52 | import org.thingsboard.server.queue.common.TbProtoQueueMsg; |
51 | import org.thingsboard.server.queue.discovery.PartitionService; | 53 | import org.thingsboard.server.queue.discovery.PartitionService; |
52 | import org.thingsboard.server.queue.provider.TbQueueProducerProvider; | 54 | import org.thingsboard.server.queue.provider.TbQueueProducerProvider; |
@@ -207,6 +209,12 @@ public class DefaultTbClusterService implements TbClusterService { | @@ -207,6 +209,12 @@ public class DefaultTbClusterService implements TbClusterService { | ||
207 | } | 209 | } |
208 | 210 | ||
209 | @Override | 211 | @Override |
212 | + public void onApiStateChange(ApiUsageState apiUsageState, TbQueueCallback callback) { | ||
213 | + onEntityChange(apiUsageState.getTenantId(), apiUsageState.getId(), apiUsageState, callback); | ||
214 | + broadcast(new ComponentLifecycleMsg(apiUsageState.getTenantId(), apiUsageState.getId(), ComponentLifecycleEvent.UPDATED)); | ||
215 | + } | ||
216 | + | ||
217 | + @Override | ||
210 | public void onDeviceProfileDelete(DeviceProfile entity, TbQueueCallback callback) { | 218 | public void onDeviceProfileDelete(DeviceProfile entity, TbQueueCallback callback) { |
211 | onEntityDelete(entity.getTenantId(), entity.getId(), entity.getName(), callback); | 219 | onEntityDelete(entity.getTenantId(), entity.getId(), entity.getName(), callback); |
212 | } | 220 | } |
@@ -221,13 +229,14 @@ public class DefaultTbClusterService implements TbClusterService { | @@ -221,13 +229,14 @@ public class DefaultTbClusterService implements TbClusterService { | ||
221 | onEntityDelete(TenantId.SYS_TENANT_ID, entity.getId(), entity.getName(), callback); | 229 | onEntityDelete(TenantId.SYS_TENANT_ID, entity.getId(), entity.getName(), callback); |
222 | } | 230 | } |
223 | 231 | ||
224 | - public <T extends HasName> void onEntityChange(TenantId tenantId, EntityId entityid, T entity, TbQueueCallback callback) { | ||
225 | - log.trace("[{}][{}][{}] Processing [{}] change event", tenantId, entityid.getEntityType(), entityid.getId(), entity.getName()); | 232 | + public <T> void onEntityChange(TenantId tenantId, EntityId entityid, T entity, TbQueueCallback callback) { |
233 | + String entityName = (entity instanceof HasName) ? ((HasName) entity).getName() : entity.getClass().getName(); | ||
234 | + log.trace("[{}][{}][{}] Processing [{}] change event", tenantId, entityid.getEntityType(), entityid.getId(), entityName); | ||
226 | TransportProtos.EntityUpdateMsg entityUpdateMsg = TransportProtos.EntityUpdateMsg.newBuilder() | 235 | TransportProtos.EntityUpdateMsg entityUpdateMsg = TransportProtos.EntityUpdateMsg.newBuilder() |
227 | .setEntityType(entityid.getEntityType().name()) | 236 | .setEntityType(entityid.getEntityType().name()) |
228 | .setData(ByteString.copyFrom(encodingService.encode(entity))).build(); | 237 | .setData(ByteString.copyFrom(encodingService.encode(entity))).build(); |
229 | ToTransportMsg transportMsg = ToTransportMsg.newBuilder().setEntityUpdateMsg(entityUpdateMsg).build(); | 238 | ToTransportMsg transportMsg = ToTransportMsg.newBuilder().setEntityUpdateMsg(entityUpdateMsg).build(); |
230 | - broadcast(transportMsg); | 239 | + broadcast(transportMsg, callback); |
231 | } | 240 | } |
232 | 241 | ||
233 | private void onEntityDelete(TenantId tenantId, EntityId entityId, String name, TbQueueCallback callback) { | 242 | private void onEntityDelete(TenantId tenantId, EntityId entityId, String name, TbQueueCallback callback) { |
@@ -238,15 +247,16 @@ public class DefaultTbClusterService implements TbClusterService { | @@ -238,15 +247,16 @@ public class DefaultTbClusterService implements TbClusterService { | ||
238 | .setEntityIdLSB(entityId.getId().getLeastSignificantBits()) | 247 | .setEntityIdLSB(entityId.getId().getLeastSignificantBits()) |
239 | .build(); | 248 | .build(); |
240 | ToTransportMsg transportMsg = ToTransportMsg.newBuilder().setEntityDeleteMsg(entityDeleteMsg).build(); | 249 | ToTransportMsg transportMsg = ToTransportMsg.newBuilder().setEntityDeleteMsg(entityDeleteMsg).build(); |
241 | - broadcast(transportMsg); | 250 | + broadcast(transportMsg, callback); |
242 | } | 251 | } |
243 | 252 | ||
244 | - private void broadcast(ToTransportMsg transportMsg) { | 253 | + private void broadcast(ToTransportMsg transportMsg, TbQueueCallback callback) { |
245 | TbQueueProducer<TbProtoQueueMsg<ToTransportMsg>> toTransportNfProducer = producerProvider.getTransportNotificationsMsgProducer(); | 254 | TbQueueProducer<TbProtoQueueMsg<ToTransportMsg>> toTransportNfProducer = producerProvider.getTransportNotificationsMsgProducer(); |
246 | Set<String> tbTransportServices = partitionService.getAllServiceIds(ServiceType.TB_TRANSPORT); | 255 | Set<String> tbTransportServices = partitionService.getAllServiceIds(ServiceType.TB_TRANSPORT); |
256 | + TbQueueCallback proxyCallback = callback != null ? new MultipleTbQueueCallbackWrapper(tbTransportServices.size(), callback) : null; | ||
247 | for (String transportServiceId : tbTransportServices) { | 257 | for (String transportServiceId : tbTransportServices) { |
248 | TopicPartitionInfo tpi = partitionService.getNotificationsTopic(ServiceType.TB_TRANSPORT, transportServiceId); | 258 | TopicPartitionInfo tpi = partitionService.getNotificationsTopic(ServiceType.TB_TRANSPORT, transportServiceId); |
249 | - toTransportNfProducer.send(tpi, new TbProtoQueueMsg<>(UUID.randomUUID(), transportMsg), null); | 259 | + toTransportNfProducer.send(tpi, new TbProtoQueueMsg<>(UUID.randomUUID(), transportMsg), proxyCallback); |
250 | toTransportNfs.incrementAndGet(); | 260 | toTransportNfs.incrementAndGet(); |
251 | } | 261 | } |
252 | } | 262 | } |
@@ -256,7 +266,8 @@ public class DefaultTbClusterService implements TbClusterService { | @@ -256,7 +266,8 @@ public class DefaultTbClusterService implements TbClusterService { | ||
256 | TbQueueProducer<TbProtoQueueMsg<ToRuleEngineNotificationMsg>> toRuleEngineProducer = producerProvider.getRuleEngineNotificationsMsgProducer(); | 266 | TbQueueProducer<TbProtoQueueMsg<ToRuleEngineNotificationMsg>> toRuleEngineProducer = producerProvider.getRuleEngineNotificationsMsgProducer(); |
257 | Set<String> tbRuleEngineServices = new HashSet<>(partitionService.getAllServiceIds(ServiceType.TB_RULE_ENGINE)); | 267 | Set<String> tbRuleEngineServices = new HashSet<>(partitionService.getAllServiceIds(ServiceType.TB_RULE_ENGINE)); |
258 | if (msg.getEntityId().getEntityType().equals(EntityType.TENANT) | 268 | if (msg.getEntityId().getEntityType().equals(EntityType.TENANT) |
259 | - || msg.getEntityId().getEntityType().equals(EntityType.DEVICE_PROFILE)) { | 269 | + || msg.getEntityId().getEntityType().equals(EntityType.DEVICE_PROFILE) |
270 | + || msg.getEntityId().getEntityType().equals(EntityType.API_USAGE_STATE)) { | ||
260 | TbQueueProducer<TbProtoQueueMsg<ToCoreNotificationMsg>> toCoreNfProducer = producerProvider.getTbCoreNotificationsMsgProducer(); | 271 | TbQueueProducer<TbProtoQueueMsg<ToCoreNotificationMsg>> toCoreNfProducer = producerProvider.getTbCoreNotificationsMsgProducer(); |
261 | Set<String> tbCoreServices = partitionService.getAllServiceIds(ServiceType.TB_CORE); | 272 | Set<String> tbCoreServices = partitionService.getAllServiceIds(ServiceType.TB_CORE); |
262 | for (String serviceId : tbCoreServices) { | 273 | for (String serviceId : tbCoreServices) { |
@@ -16,6 +16,7 @@ | @@ -16,6 +16,7 @@ | ||
16 | package org.thingsboard.server.service.queue; | 16 | package org.thingsboard.server.service.queue; |
17 | 17 | ||
18 | import org.thingsboard.rule.engine.api.msg.ToDeviceActorNotificationMsg; | 18 | import org.thingsboard.rule.engine.api.msg.ToDeviceActorNotificationMsg; |
19 | +import org.thingsboard.server.common.data.ApiUsageState; | ||
19 | import org.thingsboard.server.common.data.DeviceProfile; | 20 | import org.thingsboard.server.common.data.DeviceProfile; |
20 | import org.thingsboard.server.common.data.Tenant; | 21 | import org.thingsboard.server.common.data.Tenant; |
21 | import org.thingsboard.server.common.data.TenantProfile; | 22 | import org.thingsboard.server.common.data.TenantProfile; |
@@ -63,4 +64,6 @@ public interface TbClusterService { | @@ -63,4 +64,6 @@ public interface TbClusterService { | ||
63 | void onTenantChange(Tenant tenant, TbQueueCallback callback); | 64 | void onTenantChange(Tenant tenant, TbQueueCallback callback); |
64 | 65 | ||
65 | void onTenantDelete(Tenant tenant, TbQueueCallback callback); | 66 | void onTenantDelete(Tenant tenant, TbQueueCallback callback); |
67 | + | ||
68 | + void onApiStateChange(ApiUsageState apiUsageState, TbQueueCallback callback); | ||
66 | } | 69 | } |
@@ -5,7 +5,7 @@ | @@ -5,7 +5,7 @@ | ||
5 | * you may not use this file except in compliance with the License. | 5 | * you may not use this file except in compliance with the License. |
6 | * You may obtain a copy of the License at | 6 | * You may obtain a copy of the License at |
7 | * | 7 | * |
8 | - * http://www.apache.org/licenses/LICENSE-2.0 | 8 | + * http://www.apache.org/licenses/LICENSE-2.0 |
9 | * | 9 | * |
10 | * Unless required by applicable law or agreed to in writing, software | 10 | * Unless required by applicable law or agreed to in writing, software |
11 | * distributed under the License is distributed on an "AS IS" BASIS, | 11 | * distributed under the License is distributed on an "AS IS" BASIS, |
@@ -161,6 +161,8 @@ public abstract class AbstractConsumerService<N extends com.google.protobuf.Gene | @@ -161,6 +161,8 @@ public abstract class AbstractConsumerService<N extends com.google.protobuf.Gene | ||
161 | deviceProfileCache.evict(componentLifecycleMsg.getTenantId(), new DeviceProfileId(componentLifecycleMsg.getEntityId().getId())); | 161 | deviceProfileCache.evict(componentLifecycleMsg.getTenantId(), new DeviceProfileId(componentLifecycleMsg.getEntityId().getId())); |
162 | } else if (EntityType.DEVICE.equals(componentLifecycleMsg.getEntityId().getEntityType())) { | 162 | } else if (EntityType.DEVICE.equals(componentLifecycleMsg.getEntityId().getEntityType())) { |
163 | deviceProfileCache.evict(new DeviceId(componentLifecycleMsg.getEntityId().getId())); | 163 | deviceProfileCache.evict(new DeviceId(componentLifecycleMsg.getEntityId().getId())); |
164 | + } else if (EntityType.API_USAGE_STATE.equals(componentLifecycleMsg.getEntityId().getEntityType())) { | ||
165 | + apiUsageStateService.onApiUsageStateUpdate(componentLifecycleMsg.getTenantId()); | ||
164 | } | 166 | } |
165 | } | 167 | } |
166 | log.trace("[{}] Forwarding message to App Actor {}", id, actorMsg); | 168 | log.trace("[{}] Forwarding message to App Actor {}", id, actorMsg); |
@@ -5,7 +5,7 @@ | @@ -5,7 +5,7 @@ | ||
5 | * you may not use this file except in compliance with the License. | 5 | * you may not use this file except in compliance with the License. |
6 | * You may obtain a copy of the License at | 6 | * You may obtain a copy of the License at |
7 | * | 7 | * |
8 | - * http://www.apache.org/licenses/LICENSE-2.0 | 8 | + * http://www.apache.org/licenses/LICENSE-2.0 |
9 | * | 9 | * |
10 | * Unless required by applicable law or agreed to in writing, software | 10 | * Unless required by applicable law or agreed to in writing, software |
11 | * distributed under the License is distributed on an "AS IS" BASIS, | 11 | * distributed under the License is distributed on an "AS IS" BASIS, |
@@ -25,6 +25,7 @@ import com.google.protobuf.ByteString; | @@ -25,6 +25,7 @@ import com.google.protobuf.ByteString; | ||
25 | import lombok.extern.slf4j.Slf4j; | 25 | import lombok.extern.slf4j.Slf4j; |
26 | import org.springframework.stereotype.Service; | 26 | import org.springframework.stereotype.Service; |
27 | import org.springframework.util.StringUtils; | 27 | import org.springframework.util.StringUtils; |
28 | +import org.thingsboard.server.common.data.ApiUsageState; | ||
28 | import org.thingsboard.server.common.data.DataConstants; | 29 | import org.thingsboard.server.common.data.DataConstants; |
29 | import org.thingsboard.server.common.data.Device; | 30 | import org.thingsboard.server.common.data.Device; |
30 | import org.thingsboard.server.common.data.DeviceProfile; | 31 | import org.thingsboard.server.common.data.DeviceProfile; |
@@ -51,7 +52,6 @@ import org.thingsboard.server.dao.device.DeviceService; | @@ -51,7 +52,6 @@ import org.thingsboard.server.dao.device.DeviceService; | ||
51 | import org.thingsboard.server.dao.device.provision.ProvisionRequest; | 52 | import org.thingsboard.server.dao.device.provision.ProvisionRequest; |
52 | import org.thingsboard.server.dao.device.provision.ProvisionResponse; | 53 | import org.thingsboard.server.dao.device.provision.ProvisionResponse; |
53 | import org.thingsboard.server.dao.relation.RelationService; | 54 | import org.thingsboard.server.dao.relation.RelationService; |
54 | -import org.thingsboard.server.dao.tenant.TenantService; | ||
55 | import org.thingsboard.server.dao.util.mapping.JacksonUtil; | 55 | import org.thingsboard.server.dao.util.mapping.JacksonUtil; |
56 | import org.thingsboard.server.gen.transport.TransportProtos; | 56 | import org.thingsboard.server.gen.transport.TransportProtos; |
57 | import org.thingsboard.server.gen.transport.TransportProtos.DeviceInfoProto; | 57 | import org.thingsboard.server.gen.transport.TransportProtos.DeviceInfoProto; |
@@ -68,6 +68,7 @@ import org.thingsboard.server.gen.transport.TransportProtos.ValidateDeviceX509Ce | @@ -68,6 +68,7 @@ import org.thingsboard.server.gen.transport.TransportProtos.ValidateDeviceX509Ce | ||
68 | import org.thingsboard.server.queue.common.TbProtoQueueMsg; | 68 | import org.thingsboard.server.queue.common.TbProtoQueueMsg; |
69 | import org.thingsboard.server.queue.util.TbCoreComponent; | 69 | import org.thingsboard.server.queue.util.TbCoreComponent; |
70 | import org.thingsboard.server.dao.device.provision.ProvisionFailedException; | 70 | import org.thingsboard.server.dao.device.provision.ProvisionFailedException; |
71 | +import org.thingsboard.server.service.apiusage.TbApiUsageStateService; | ||
71 | import org.thingsboard.server.service.executors.DbCallbackExecutorService; | 72 | import org.thingsboard.server.service.executors.DbCallbackExecutorService; |
72 | import org.thingsboard.server.service.profile.TbDeviceProfileCache; | 73 | import org.thingsboard.server.service.profile.TbDeviceProfileCache; |
73 | import org.thingsboard.server.service.profile.TbTenantProfileCache; | 74 | import org.thingsboard.server.service.profile.TbTenantProfileCache; |
@@ -92,6 +93,7 @@ public class DefaultTransportApiService implements TransportApiService { | @@ -92,6 +93,7 @@ public class DefaultTransportApiService implements TransportApiService { | ||
92 | 93 | ||
93 | private final TbDeviceProfileCache deviceProfileCache; | 94 | private final TbDeviceProfileCache deviceProfileCache; |
94 | private final TbTenantProfileCache tenantProfileCache; | 95 | private final TbTenantProfileCache tenantProfileCache; |
96 | + private final TbApiUsageStateService apiUsageStateService; | ||
95 | private final DeviceService deviceService; | 97 | private final DeviceService deviceService; |
96 | private final RelationService relationService; | 98 | private final RelationService relationService; |
97 | private final DeviceCredentialsService deviceCredentialsService; | 99 | private final DeviceCredentialsService deviceCredentialsService; |
@@ -104,13 +106,14 @@ public class DefaultTransportApiService implements TransportApiService { | @@ -104,13 +106,14 @@ public class DefaultTransportApiService implements TransportApiService { | ||
104 | private final ConcurrentMap<String, ReentrantLock> deviceCreationLocks = new ConcurrentHashMap<>(); | 106 | private final ConcurrentMap<String, ReentrantLock> deviceCreationLocks = new ConcurrentHashMap<>(); |
105 | 107 | ||
106 | public DefaultTransportApiService(TbDeviceProfileCache deviceProfileCache, | 108 | public DefaultTransportApiService(TbDeviceProfileCache deviceProfileCache, |
107 | - TbTenantProfileCache tenantProfileCache, DeviceService deviceService, | 109 | + TbTenantProfileCache tenantProfileCache, TbApiUsageStateService apiUsageStateService, DeviceService deviceService, |
108 | RelationService relationService, DeviceCredentialsService deviceCredentialsService, | 110 | RelationService relationService, DeviceCredentialsService deviceCredentialsService, |
109 | DeviceStateService deviceStateService, DbCallbackExecutorService dbCallbackExecutorService, | 111 | DeviceStateService deviceStateService, DbCallbackExecutorService dbCallbackExecutorService, |
110 | TbClusterService tbClusterService, DataDecodingEncodingService dataDecodingEncodingService, | 112 | TbClusterService tbClusterService, DataDecodingEncodingService dataDecodingEncodingService, |
111 | DeviceProvisionService deviceProvisionService) { | 113 | DeviceProvisionService deviceProvisionService) { |
112 | this.deviceProfileCache = deviceProfileCache; | 114 | this.deviceProfileCache = deviceProfileCache; |
113 | this.tenantProfileCache = tenantProfileCache; | 115 | this.tenantProfileCache = tenantProfileCache; |
116 | + this.apiUsageStateService = apiUsageStateService; | ||
114 | this.deviceService = deviceService; | 117 | this.deviceService = deviceService; |
115 | this.relationService = relationService; | 118 | this.relationService = relationService; |
116 | this.deviceCredentialsService = deviceCredentialsService; | 119 | this.deviceCredentialsService = deviceCredentialsService; |
@@ -316,18 +319,21 @@ public class DefaultTransportApiService implements TransportApiService { | @@ -316,18 +319,21 @@ public class DefaultTransportApiService implements TransportApiService { | ||
316 | private ListenableFuture<TransportApiResponseMsg> handle(GetEntityProfileRequestMsg requestMsg) { | 319 | private ListenableFuture<TransportApiResponseMsg> handle(GetEntityProfileRequestMsg requestMsg) { |
317 | EntityType entityType = EntityType.valueOf(requestMsg.getEntityType()); | 320 | EntityType entityType = EntityType.valueOf(requestMsg.getEntityType()); |
318 | UUID entityUuid = new UUID(requestMsg.getEntityIdMSB(), requestMsg.getEntityIdLSB()); | 321 | UUID entityUuid = new UUID(requestMsg.getEntityIdMSB(), requestMsg.getEntityIdLSB()); |
319 | - ByteString data; | 322 | + GetEntityProfileResponseMsg.Builder builder = GetEntityProfileResponseMsg.newBuilder(); |
320 | if (entityType.equals(EntityType.DEVICE_PROFILE)) { | 323 | if (entityType.equals(EntityType.DEVICE_PROFILE)) { |
321 | DeviceProfileId deviceProfileId = new DeviceProfileId(entityUuid); | 324 | DeviceProfileId deviceProfileId = new DeviceProfileId(entityUuid); |
322 | DeviceProfile deviceProfile = deviceProfileCache.find(deviceProfileId); | 325 | DeviceProfile deviceProfile = deviceProfileCache.find(deviceProfileId); |
323 | - data = ByteString.copyFrom(dataDecodingEncodingService.encode(deviceProfile)); | 326 | + builder.setData(ByteString.copyFrom(dataDecodingEncodingService.encode(deviceProfile))); |
324 | } else if (entityType.equals(EntityType.TENANT)) { | 327 | } else if (entityType.equals(EntityType.TENANT)) { |
325 | - TenantProfile tenantProfile = tenantProfileCache.get(new TenantId(entityUuid)); | ||
326 | - data = ByteString.copyFrom(dataDecodingEncodingService.encode(tenantProfile)); | 328 | + TenantId tenantId = new TenantId(entityUuid); |
329 | + TenantProfile tenantProfile = tenantProfileCache.get(tenantId); | ||
330 | + ApiUsageState state = apiUsageStateService.getApiUsageState(tenantId); | ||
331 | + builder.setData(ByteString.copyFrom(dataDecodingEncodingService.encode(tenantProfile))); | ||
332 | + builder.setApiState(ByteString.copyFrom(dataDecodingEncodingService.encode(state))); | ||
327 | } else { | 333 | } else { |
328 | throw new RuntimeException("Invalid entity profile request: " + entityType); | 334 | throw new RuntimeException("Invalid entity profile request: " + entityType); |
329 | } | 335 | } |
330 | - return Futures.immediateFuture(TransportApiResponseMsg.newBuilder().setEntityProfileResponseMsg(GetEntityProfileResponseMsg.newBuilder().setData(data).build()).build()); | 336 | + return Futures.immediateFuture(TransportApiResponseMsg.newBuilder().setEntityProfileResponseMsg(builder).build()); |
331 | } | 337 | } |
332 | 338 | ||
333 | private ListenableFuture<TransportApiResponseMsg> getDeviceInfo(DeviceId deviceId, DeviceCredentials credentials) { | 339 | private ListenableFuture<TransportApiResponseMsg> getDeviceInfo(DeviceId deviceId, DeviceCredentials credentials) { |
@@ -18,7 +18,6 @@ package org.thingsboard.server.common.data; | @@ -18,7 +18,6 @@ package org.thingsboard.server.common.data; | ||
18 | public enum ApiUsageRecordKey { | 18 | public enum ApiUsageRecordKey { |
19 | 19 | ||
20 | MSG_COUNT, | 20 | MSG_COUNT, |
21 | - MSG_BYTES_COUNT, | ||
22 | DP_TRANSPORT_COUNT, | 21 | DP_TRANSPORT_COUNT, |
23 | DP_STORAGE_COUNT, | 22 | DP_STORAGE_COUNT, |
24 | RE_EXEC_COUNT, | 23 | RE_EXEC_COUNT, |
@@ -29,18 +29,24 @@ public class ApiUsageState extends BaseData<ApiUsageStateId> implements HasTenan | @@ -29,18 +29,24 @@ public class ApiUsageState extends BaseData<ApiUsageStateId> implements HasTenan | ||
29 | 29 | ||
30 | private static final long serialVersionUID = 8250339805336035966L; | 30 | private static final long serialVersionUID = 8250339805336035966L; |
31 | 31 | ||
32 | - @Getter @Setter | 32 | + @Getter |
33 | + @Setter | ||
33 | private TenantId tenantId; | 34 | private TenantId tenantId; |
34 | - @Getter @Setter | 35 | + @Getter |
36 | + @Setter | ||
35 | private EntityId entityId; | 37 | private EntityId entityId; |
36 | - @Getter @Setter | ||
37 | - private boolean transportEnabled; | ||
38 | - @Getter @Setter | ||
39 | - private boolean dbStorageEnabled; | ||
40 | - @Getter @Setter | ||
41 | - private boolean ruleEngineEnabled; | ||
42 | - @Getter @Setter | ||
43 | - private boolean jsExecEnabled; | 38 | + @Getter |
39 | + @Setter | ||
40 | + private boolean transportEnabled = true; | ||
41 | + @Getter | ||
42 | + @Setter | ||
43 | + private boolean dbStorageEnabled = true; | ||
44 | + @Getter | ||
45 | + @Setter | ||
46 | + private boolean reExecEnabled = true; | ||
47 | + @Getter | ||
48 | + @Setter | ||
49 | + private boolean jsExecEnabled = true; | ||
44 | 50 | ||
45 | public ApiUsageState() { | 51 | public ApiUsageState() { |
46 | super(); | 52 | super(); |
@@ -54,5 +60,9 @@ public class ApiUsageState extends BaseData<ApiUsageStateId> implements HasTenan | @@ -54,5 +60,9 @@ public class ApiUsageState extends BaseData<ApiUsageStateId> implements HasTenan | ||
54 | super(ur); | 60 | super(ur); |
55 | this.tenantId = ur.getTenantId(); | 61 | this.tenantId = ur.getTenantId(); |
56 | this.entityId = ur.getEntityId(); | 62 | this.entityId = ur.getEntityId(); |
63 | + this.transportEnabled = ur.isTransportEnabled(); | ||
64 | + this.dbStorageEnabled = ur.isDbStorageEnabled(); | ||
65 | + this.reExecEnabled = ur.isReExecEnabled(); | ||
66 | + this.jsExecEnabled = ur.isJsExecEnabled(); | ||
57 | } | 67 | } |
58 | } | 68 | } |
common/queue/src/main/java/org/thingsboard/server/queue/common/MultipleTbQueueCallbackWrapper.java
0 → 100644
1 | +/** | ||
2 | + * Copyright © 2016-2020 The Thingsboard Authors | ||
3 | + * | ||
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | + * you may not use this file except in compliance with the License. | ||
6 | + * You may obtain a copy of the License at | ||
7 | + * | ||
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | + * | ||
10 | + * Unless required by applicable law or agreed to in writing, software | ||
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | + * See the License for the specific language governing permissions and | ||
14 | + * limitations under the License. | ||
15 | + */ | ||
16 | +package org.thingsboard.server.queue.common; | ||
17 | + | ||
18 | +import org.thingsboard.server.common.msg.queue.RuleEngineException; | ||
19 | +import org.thingsboard.server.queue.TbQueueCallback; | ||
20 | +import org.thingsboard.server.queue.TbQueueMsgMetadata; | ||
21 | + | ||
22 | +import java.util.concurrent.atomic.AtomicInteger; | ||
23 | + | ||
24 | +public class MultipleTbQueueCallbackWrapper implements TbQueueCallback { | ||
25 | + | ||
26 | + private final AtomicInteger tbQueueCallbackCount; | ||
27 | + private final TbQueueCallback callback; | ||
28 | + | ||
29 | + public MultipleTbQueueCallbackWrapper(int tbQueueCallbackCount, TbQueueCallback callback) { | ||
30 | + this.tbQueueCallbackCount = new AtomicInteger(tbQueueCallbackCount); | ||
31 | + this.callback = callback; | ||
32 | + } | ||
33 | + | ||
34 | + @Override | ||
35 | + public void onSuccess(TbQueueMsgMetadata metadata) { | ||
36 | + if (tbQueueCallbackCount.decrementAndGet() <= 0) { | ||
37 | + callback.onSuccess(metadata); | ||
38 | + } | ||
39 | + } | ||
40 | + | ||
41 | + @Override | ||
42 | + public void onFailure(Throwable t) { | ||
43 | + callback.onFailure(new RuleEngineException(t.getMessage())); | ||
44 | + } | ||
45 | +} |
@@ -186,6 +186,7 @@ message GetEntityProfileRequestMsg { | @@ -186,6 +186,7 @@ message GetEntityProfileRequestMsg { | ||
186 | message GetEntityProfileResponseMsg { | 186 | message GetEntityProfileResponseMsg { |
187 | string entityType = 1; | 187 | string entityType = 1; |
188 | bytes data = 2; | 188 | bytes data = 2; |
189 | + bytes apiState = 3; | ||
189 | } | 190 | } |
190 | 191 | ||
191 | message EntityUpdateMsg { | 192 | message EntityUpdateMsg { |
@@ -5,7 +5,7 @@ | @@ -5,7 +5,7 @@ | ||
5 | * you may not use this file except in compliance with the License. | 5 | * you may not use this file except in compliance with the License. |
6 | * You may obtain a copy of the License at | 6 | * You may obtain a copy of the License at |
7 | * | 7 | * |
8 | - * http://www.apache.org/licenses/LICENSE-2.0 | 8 | + * http://www.apache.org/licenses/LICENSE-2.0 |
9 | * | 9 | * |
10 | * Unless required by applicable law or agreed to in writing, software | 10 | * Unless required by applicable law or agreed to in writing, software |
11 | * distributed under the License is distributed on an "AS IS" BASIS, | 11 | * distributed under the License is distributed on an "AS IS" BASIS, |
@@ -45,6 +45,7 @@ import org.thingsboard.server.common.data.DeviceTransportType; | @@ -45,6 +45,7 @@ import org.thingsboard.server.common.data.DeviceTransportType; | ||
45 | import org.thingsboard.server.common.data.TransportPayloadType; | 45 | import org.thingsboard.server.common.data.TransportPayloadType; |
46 | import org.thingsboard.server.common.data.device.profile.MqttTopics; | 46 | import org.thingsboard.server.common.data.device.profile.MqttTopics; |
47 | import org.thingsboard.server.common.msg.EncryptionUtil; | 47 | import org.thingsboard.server.common.msg.EncryptionUtil; |
48 | +import org.thingsboard.server.common.msg.tools.TbRateLimitsException; | ||
48 | import org.thingsboard.server.common.transport.SessionMsgListener; | 49 | import org.thingsboard.server.common.transport.SessionMsgListener; |
49 | import org.thingsboard.server.common.transport.TransportService; | 50 | import org.thingsboard.server.common.transport.TransportService; |
50 | import org.thingsboard.server.common.transport.TransportServiceCallback; | 51 | import org.thingsboard.server.common.transport.TransportServiceCallback; |
@@ -630,7 +631,11 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement | @@ -630,7 +631,11 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement | ||
630 | 631 | ||
631 | @Override | 632 | @Override |
632 | public void onError(Throwable e) { | 633 | public void onError(Throwable e) { |
633 | - log.warn("[{}] Failed to submit session event", sessionId, e); | 634 | + if (e instanceof TbRateLimitsException) { |
635 | + log.trace("[{}] Failed to submit session event", sessionId, e); | ||
636 | + } else { | ||
637 | + log.warn("[{}] Failed to submit session event", sessionId, e); | ||
638 | + } | ||
634 | ctx.writeAndFlush(createMqttConnAckMsg(MqttConnectReturnCode.CONNECTION_REFUSED_SERVER_UNAVAILABLE)); | 639 | ctx.writeAndFlush(createMqttConnAckMsg(MqttConnectReturnCode.CONNECTION_REFUSED_SERVER_UNAVAILABLE)); |
635 | ctx.close(); | 640 | ctx.close(); |
636 | } | 641 | } |
@@ -45,7 +45,7 @@ import org.thingsboard.server.gen.transport.TransportProtos.ValidateDeviceX509Ce | @@ -45,7 +45,7 @@ import org.thingsboard.server.gen.transport.TransportProtos.ValidateDeviceX509Ce | ||
45 | */ | 45 | */ |
46 | public interface TransportService { | 46 | public interface TransportService { |
47 | 47 | ||
48 | - GetEntityProfileResponseMsg getRoutingInfo(GetEntityProfileRequestMsg msg); | 48 | + GetEntityProfileResponseMsg getEntityProfile(GetEntityProfileRequestMsg msg); |
49 | 49 | ||
50 | void process(DeviceTransportType transportType, ValidateDeviceTokenRequestMsg msg, | 50 | void process(DeviceTransportType transportType, ValidateDeviceTokenRequestMsg msg, |
51 | TransportServiceCallback<ValidateDeviceCredentialsResponse> callback); | 51 | TransportServiceCallback<ValidateDeviceCredentialsResponse> callback); |
@@ -62,8 +62,6 @@ public interface TransportService { | @@ -62,8 +62,6 @@ public interface TransportService { | ||
62 | void process(ProvisionDeviceRequestMsg msg, | 62 | void process(ProvisionDeviceRequestMsg msg, |
63 | TransportServiceCallback<ProvisionDeviceResponseMsg> callback); | 63 | TransportServiceCallback<ProvisionDeviceResponseMsg> callback); |
64 | 64 | ||
65 | - void onProfileUpdate(DeviceProfile deviceProfile); | ||
66 | - | ||
67 | boolean checkLimits(SessionInfoProto sessionInfo, Object msg, TransportServiceCallback<Void> callback); | 65 | boolean checkLimits(SessionInfoProto sessionInfo, Object msg, TransportServiceCallback<Void> callback); |
68 | 66 | ||
69 | boolean checkLimits(SessionInfoProto sessionInfo, Object msg, TransportServiceCallback<Void> callback, int dataPoints, TransportRateLimitType... limits); | 67 | boolean checkLimits(SessionInfoProto sessionInfo, Object msg, TransportServiceCallback<Void> callback, int dataPoints, TransportRateLimitType... limits); |
@@ -5,7 +5,7 @@ | @@ -5,7 +5,7 @@ | ||
5 | * you may not use this file except in compliance with the License. | 5 | * you may not use this file except in compliance with the License. |
6 | * You may obtain a copy of the License at | 6 | * You may obtain a copy of the License at |
7 | * | 7 | * |
8 | - * http://www.apache.org/licenses/LICENSE-2.0 | 8 | + * http://www.apache.org/licenses/LICENSE-2.0 |
9 | * | 9 | * |
10 | * Unless required by applicable law or agreed to in writing, software | 10 | * Unless required by applicable law or agreed to in writing, software |
11 | * distributed under the License is distributed on an "AS IS" BASIS, | 11 | * distributed under the License is distributed on an "AS IS" BASIS, |
@@ -33,6 +33,7 @@ import java.util.concurrent.ConcurrentMap; | @@ -33,6 +33,7 @@ import java.util.concurrent.ConcurrentMap; | ||
33 | @Slf4j | 33 | @Slf4j |
34 | public class DefaultTransportRateLimitService implements TransportRateLimitService { | 34 | public class DefaultTransportRateLimitService implements TransportRateLimitService { |
35 | 35 | ||
36 | + private final ConcurrentMap<TenantId, Boolean> tenantAllowed = new ConcurrentHashMap<>(); | ||
36 | private final ConcurrentMap<TenantId, TransportRateLimit[]> perTenantLimits = new ConcurrentHashMap<>(); | 37 | private final ConcurrentMap<TenantId, TransportRateLimit[]> perTenantLimits = new ConcurrentHashMap<>(); |
37 | private final ConcurrentMap<DeviceId, TransportRateLimit[]> perDeviceLimits = new ConcurrentHashMap<>(); | 38 | private final ConcurrentMap<DeviceId, TransportRateLimit[]> perDeviceLimits = new ConcurrentHashMap<>(); |
38 | 39 | ||
@@ -46,6 +47,9 @@ public class DefaultTransportRateLimitService implements TransportRateLimitServi | @@ -46,6 +47,9 @@ public class DefaultTransportRateLimitService implements TransportRateLimitServi | ||
46 | 47 | ||
47 | @Override | 48 | @Override |
48 | public TransportRateLimitType checkLimits(TenantId tenantId, DeviceId deviceId, int dataPoints, TransportRateLimitType... limits) { | 49 | public TransportRateLimitType checkLimits(TenantId tenantId, DeviceId deviceId, int dataPoints, TransportRateLimitType... limits) { |
50 | + if (!tenantAllowed.getOrDefault(tenantId, Boolean.TRUE)) { | ||
51 | + return TransportRateLimitType.TENANT_ADDED_TO_DISABLED_LIST; | ||
52 | + } | ||
49 | TransportRateLimit[] tenantLimits = getTenantRateLimits(tenantId); | 53 | TransportRateLimit[] tenantLimits = getTenantRateLimits(tenantId); |
50 | TransportRateLimit[] deviceLimits = getDeviceRateLimits(tenantId, deviceId); | 54 | TransportRateLimit[] deviceLimits = getDeviceRateLimits(tenantId, deviceId); |
51 | for (TransportRateLimitType limitType : limits) { | 55 | for (TransportRateLimitType limitType : limits) { |
@@ -85,6 +89,11 @@ public class DefaultTransportRateLimitService implements TransportRateLimitServi | @@ -85,6 +89,11 @@ public class DefaultTransportRateLimitService implements TransportRateLimitServi | ||
85 | perDeviceLimits.remove(deviceId); | 89 | perDeviceLimits.remove(deviceId); |
86 | } | 90 | } |
87 | 91 | ||
92 | + @Override | ||
93 | + public void update(TenantId tenantId, boolean allowed) { | ||
94 | + tenantAllowed.put(tenantId, allowed); | ||
95 | + } | ||
96 | + | ||
88 | private void mergeLimits(TenantId tenantId, TransportRateLimit[] newRateLimits) { | 97 | private void mergeLimits(TenantId tenantId, TransportRateLimit[] newRateLimits) { |
89 | TransportRateLimit[] oldRateLimits = perTenantLimits.get(tenantId); | 98 | TransportRateLimit[] oldRateLimits = perTenantLimits.get(tenantId); |
90 | if (oldRateLimits == null) { | 99 | if (oldRateLimits == null) { |
@@ -5,7 +5,7 @@ | @@ -5,7 +5,7 @@ | ||
5 | * you may not use this file except in compliance with the License. | 5 | * you may not use this file except in compliance with the License. |
6 | * You may obtain a copy of the License at | 6 | * You may obtain a copy of the License at |
7 | * | 7 | * |
8 | - * http://www.apache.org/licenses/LICENSE-2.0 | 8 | + * http://www.apache.org/licenses/LICENSE-2.0 |
9 | * | 9 | * |
10 | * Unless required by applicable law or agreed to in writing, software | 10 | * Unless required by applicable law or agreed to in writing, software |
11 | * distributed under the License is distributed on an "AS IS" BASIS, | 11 | * distributed under the License is distributed on an "AS IS" BASIS, |
@@ -19,6 +19,7 @@ import lombok.Getter; | @@ -19,6 +19,7 @@ import lombok.Getter; | ||
19 | 19 | ||
20 | public enum TransportRateLimitType { | 20 | public enum TransportRateLimitType { |
21 | 21 | ||
22 | + TENANT_ADDED_TO_DISABLED_LIST("general.tenant.disabled", true, false), | ||
22 | TENANT_MAX_MSGS("transport.tenant.msg", true, true), | 23 | TENANT_MAX_MSGS("transport.tenant.msg", true, true), |
23 | TENANT_TELEMETRY_MSGS("transport.tenant.telemetry", true, true), | 24 | TENANT_TELEMETRY_MSGS("transport.tenant.telemetry", true, true), |
24 | TENANT_MAX_DATA_POINTS("transport.tenant.dataPoints", true, false), | 25 | TENANT_MAX_DATA_POINTS("transport.tenant.dataPoints", true, false), |
@@ -5,7 +5,7 @@ | @@ -5,7 +5,7 @@ | ||
5 | * you may not use this file except in compliance with the License. | 5 | * you may not use this file except in compliance with the License. |
6 | * You may obtain a copy of the License at | 6 | * You may obtain a copy of the License at |
7 | * | 7 | * |
8 | - * http://www.apache.org/licenses/LICENSE-2.0 | 8 | + * http://www.apache.org/licenses/LICENSE-2.0 |
9 | * | 9 | * |
10 | * Unless required by applicable law or agreed to in writing, software | 10 | * Unless required by applicable law or agreed to in writing, software |
11 | * distributed under the License is distributed on an "AS IS" BASIS, | 11 | * distributed under the License is distributed on an "AS IS" BASIS, |
@@ -26,6 +26,7 @@ import org.springframework.beans.factory.annotation.Value; | @@ -26,6 +26,7 @@ import org.springframework.beans.factory.annotation.Value; | ||
26 | import org.springframework.stereotype.Service; | 26 | import org.springframework.stereotype.Service; |
27 | import org.thingsboard.common.util.ThingsBoardThreadFactory; | 27 | import org.thingsboard.common.util.ThingsBoardThreadFactory; |
28 | import org.thingsboard.server.common.data.ApiUsageRecordKey; | 28 | import org.thingsboard.server.common.data.ApiUsageRecordKey; |
29 | +import org.thingsboard.server.common.data.ApiUsageState; | ||
29 | import org.thingsboard.server.common.data.DeviceProfile; | 30 | import org.thingsboard.server.common.data.DeviceProfile; |
30 | import org.thingsboard.server.common.data.DeviceTransportType; | 31 | import org.thingsboard.server.common.data.DeviceTransportType; |
31 | import org.thingsboard.server.common.data.EntityType; | 32 | import org.thingsboard.server.common.data.EntityType; |
@@ -237,7 +238,7 @@ public class DefaultTransportService implements TransportService { | @@ -237,7 +238,7 @@ public class DefaultTransportService implements TransportService { | ||
237 | } | 238 | } |
238 | 239 | ||
239 | @Override | 240 | @Override |
240 | - public TransportProtos.GetEntityProfileResponseMsg getRoutingInfo(TransportProtos.GetEntityProfileRequestMsg msg) { | 241 | + public TransportProtos.GetEntityProfileResponseMsg getEntityProfile(TransportProtos.GetEntityProfileRequestMsg msg) { |
241 | TbProtoQueueMsg<TransportProtos.TransportApiRequestMsg> protoMsg = | 242 | TbProtoQueueMsg<TransportProtos.TransportApiRequestMsg> protoMsg = |
242 | new TbProtoQueueMsg<>(UUID.randomUUID(), TransportProtos.TransportApiRequestMsg.newBuilder().setEntityProfileRequestMsg(msg).build()); | 243 | new TbProtoQueueMsg<>(UUID.randomUUID(), TransportProtos.TransportApiRequestMsg.newBuilder().setEntityProfileRequestMsg(msg).build()); |
243 | try { | 244 | try { |
@@ -641,8 +642,7 @@ public class DefaultTransportService implements TransportService { | @@ -641,8 +642,7 @@ public class DefaultTransportService implements TransportService { | ||
641 | onProfileUpdate(deviceProfile); | 642 | onProfileUpdate(deviceProfile); |
642 | } | 643 | } |
643 | } else if (EntityType.TENANT_PROFILE.equals(entityType)) { | 644 | } else if (EntityType.TENANT_PROFILE.equals(entityType)) { |
644 | - TenantProfileUpdateResult update = tenantProfileCache.put(msg.getData()); | ||
645 | - rateLimitService.update(update); | 645 | + rateLimitService.update(tenantProfileCache.put(msg.getData())); |
646 | } else if (EntityType.TENANT.equals(entityType)) { | 646 | } else if (EntityType.TENANT.equals(entityType)) { |
647 | Optional<Tenant> profileOpt = dataDecodingEncodingService.decode(msg.getData().toByteArray()); | 647 | Optional<Tenant> profileOpt = dataDecodingEncodingService.decode(msg.getData().toByteArray()); |
648 | if (profileOpt.isPresent()) { | 648 | if (profileOpt.isPresent()) { |
@@ -652,6 +652,12 @@ public class DefaultTransportService implements TransportService { | @@ -652,6 +652,12 @@ public class DefaultTransportService implements TransportService { | ||
652 | rateLimitService.update(tenant.getId()); | 652 | rateLimitService.update(tenant.getId()); |
653 | } | 653 | } |
654 | } | 654 | } |
655 | + } else if (EntityType.API_USAGE_STATE.equals(entityType)) { | ||
656 | + Optional<ApiUsageState> stateOpt = dataDecodingEncodingService.decode(msg.getData().toByteArray()); | ||
657 | + if (stateOpt.isPresent()) { | ||
658 | + ApiUsageState apiUsageState = stateOpt.get(); | ||
659 | + rateLimitService.update(apiUsageState.getTenantId(), apiUsageState.isTransportEnabled()); | ||
660 | + } | ||
655 | } | 661 | } |
656 | } else if (toSessionMsg.hasEntityDeleteMsg()) { | 662 | } else if (toSessionMsg.hasEntityDeleteMsg()) { |
657 | TransportProtos.EntityDeleteMsg msg = toSessionMsg.getEntityDeleteMsg(); | 663 | TransportProtos.EntityDeleteMsg msg = toSessionMsg.getEntityDeleteMsg(); |
@@ -673,8 +679,7 @@ public class DefaultTransportService implements TransportService { | @@ -673,8 +679,7 @@ public class DefaultTransportService implements TransportService { | ||
673 | } | 679 | } |
674 | } | 680 | } |
675 | 681 | ||
676 | - @Override | ||
677 | - public void onProfileUpdate(DeviceProfile deviceProfile) { | 682 | + private void onProfileUpdate(DeviceProfile deviceProfile) { |
678 | long deviceProfileIdMSB = deviceProfile.getId().getId().getMostSignificantBits(); | 683 | long deviceProfileIdMSB = deviceProfile.getId().getId().getMostSignificantBits(); |
679 | long deviceProfileIdLSB = deviceProfile.getId().getId().getLeastSignificantBits(); | 684 | long deviceProfileIdLSB = deviceProfile.getId().getId().getLeastSignificantBits(); |
680 | sessions.forEach((id, md) -> { | 685 | sessions.forEach((id, md) -> { |
@@ -5,7 +5,7 @@ | @@ -5,7 +5,7 @@ | ||
5 | * you may not use this file except in compliance with the License. | 5 | * you may not use this file except in compliance with the License. |
6 | * You may obtain a copy of the License at | 6 | * You may obtain a copy of the License at |
7 | * | 7 | * |
8 | - * http://www.apache.org/licenses/LICENSE-2.0 | 8 | + * http://www.apache.org/licenses/LICENSE-2.0 |
9 | * | 9 | * |
10 | * Unless required by applicable law or agreed to in writing, software | 10 | * Unless required by applicable law or agreed to in writing, software |
11 | * distributed under the License is distributed on an "AS IS" BASIS, | 11 | * distributed under the License is distributed on an "AS IS" BASIS, |
@@ -18,21 +18,19 @@ package org.thingsboard.server.common.transport.service; | @@ -18,21 +18,19 @@ package org.thingsboard.server.common.transport.service; | ||
18 | import com.google.protobuf.ByteString; | 18 | import com.google.protobuf.ByteString; |
19 | import lombok.extern.slf4j.Slf4j; | 19 | import lombok.extern.slf4j.Slf4j; |
20 | import org.springframework.beans.factory.annotation.Autowired; | 20 | import org.springframework.beans.factory.annotation.Autowired; |
21 | -import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; | ||
22 | import org.springframework.context.annotation.Lazy; | 21 | import org.springframework.context.annotation.Lazy; |
23 | import org.springframework.stereotype.Component; | 22 | import org.springframework.stereotype.Component; |
24 | -import org.thingsboard.server.common.data.DeviceProfile; | 23 | +import org.thingsboard.server.common.data.ApiUsageState; |
25 | import org.thingsboard.server.common.data.EntityType; | 24 | import org.thingsboard.server.common.data.EntityType; |
26 | import org.thingsboard.server.common.data.TenantProfile; | 25 | import org.thingsboard.server.common.data.TenantProfile; |
27 | import org.thingsboard.server.common.data.id.TenantId; | 26 | import org.thingsboard.server.common.data.id.TenantId; |
28 | import org.thingsboard.server.common.data.id.TenantProfileId; | 27 | import org.thingsboard.server.common.data.id.TenantProfileId; |
29 | import org.thingsboard.server.common.transport.TransportService; | 28 | import org.thingsboard.server.common.transport.TransportService; |
30 | import org.thingsboard.server.common.transport.TransportTenantProfileCache; | 29 | import org.thingsboard.server.common.transport.TransportTenantProfileCache; |
30 | +import org.thingsboard.server.common.transport.limits.TransportRateLimitService; | ||
31 | import org.thingsboard.server.common.transport.profile.TenantProfileUpdateResult; | 31 | import org.thingsboard.server.common.transport.profile.TenantProfileUpdateResult; |
32 | import org.thingsboard.server.common.transport.util.DataDecodingEncodingService; | 32 | import org.thingsboard.server.common.transport.util.DataDecodingEncodingService; |
33 | import org.thingsboard.server.gen.transport.TransportProtos; | 33 | import org.thingsboard.server.gen.transport.TransportProtos; |
34 | -import org.thingsboard.server.queue.discovery.TenantRoutingInfo; | ||
35 | -import org.thingsboard.server.queue.discovery.TenantRoutingInfoService; | ||
36 | import org.thingsboard.server.queue.util.TbTransportComponent; | 34 | import org.thingsboard.server.queue.util.TbTransportComponent; |
37 | 35 | ||
38 | import java.util.Collections; | 36 | import java.util.Collections; |
@@ -54,10 +52,17 @@ public class DefaultTransportTenantProfileCache implements TransportTenantProfil | @@ -54,10 +52,17 @@ public class DefaultTransportTenantProfileCache implements TransportTenantProfil | ||
54 | private final ConcurrentMap<TenantProfileId, Set<TenantId>> tenantProfileIds = new ConcurrentHashMap<>(); | 52 | private final ConcurrentMap<TenantProfileId, Set<TenantId>> tenantProfileIds = new ConcurrentHashMap<>(); |
55 | private final DataDecodingEncodingService dataDecodingEncodingService; | 53 | private final DataDecodingEncodingService dataDecodingEncodingService; |
56 | 54 | ||
55 | + private TransportRateLimitService rateLimitService; | ||
57 | private TransportService transportService; | 56 | private TransportService transportService; |
58 | 57 | ||
59 | @Lazy | 58 | @Lazy |
60 | @Autowired | 59 | @Autowired |
60 | + public void setRateLimitService(TransportRateLimitService rateLimitService) { | ||
61 | + this.rateLimitService = rateLimitService; | ||
62 | + } | ||
63 | + | ||
64 | + @Lazy | ||
65 | + @Autowired | ||
61 | public void setTransportService(TransportService transportService) { | 66 | public void setTransportService(TransportService transportService) { |
62 | this.transportService = transportService; | 67 | this.transportService = transportService; |
63 | } | 68 | } |
@@ -77,7 +82,8 @@ public class DefaultTransportTenantProfileCache implements TransportTenantProfil | @@ -77,7 +82,8 @@ public class DefaultTransportTenantProfileCache implements TransportTenantProfil | ||
77 | if (profileOpt.isPresent()) { | 82 | if (profileOpt.isPresent()) { |
78 | TenantProfile newProfile = profileOpt.get(); | 83 | TenantProfile newProfile = profileOpt.get(); |
79 | log.trace("[{}] put: {}", newProfile.getId(), newProfile); | 84 | log.trace("[{}] put: {}", newProfile.getId(), newProfile); |
80 | - return new TenantProfileUpdateResult(newProfile, tenantProfileIds.get(newProfile.getId())); | 85 | + Set<TenantId> affectedTenants = tenantProfileIds.get(newProfile.getId()); |
86 | + return new TenantProfileUpdateResult(newProfile, affectedTenants != null ? affectedTenants : Collections.emptySet()); | ||
81 | } else { | 87 | } else { |
82 | log.warn("Failed to decode profile: {}", profileBody.toString()); | 88 | log.warn("Failed to decode profile: {}", profileBody.toString()); |
83 | return new TenantProfileUpdateResult(null, Collections.emptySet()); | 89 | return new TenantProfileUpdateResult(null, Collections.emptySet()); |
@@ -127,8 +133,8 @@ public class DefaultTransportTenantProfileCache implements TransportTenantProfil | @@ -127,8 +133,8 @@ public class DefaultTransportTenantProfileCache implements TransportTenantProfil | ||
127 | .setEntityIdMSB(tenantId.getId().getMostSignificantBits()) | 133 | .setEntityIdMSB(tenantId.getId().getMostSignificantBits()) |
128 | .setEntityIdLSB(tenantId.getId().getLeastSignificantBits()) | 134 | .setEntityIdLSB(tenantId.getId().getLeastSignificantBits()) |
129 | .build(); | 135 | .build(); |
130 | - TransportProtos.GetEntityProfileResponseMsg routingInfo = transportService.getRoutingInfo(msg); | ||
131 | - Optional<TenantProfile> profileOpt = dataDecodingEncodingService.decode(routingInfo.getData().toByteArray()); | 136 | + TransportProtos.GetEntityProfileResponseMsg entityProfileMsg = transportService.getEntityProfile(msg); |
137 | + Optional<TenantProfile> profileOpt = dataDecodingEncodingService.decode(entityProfileMsg.getData().toByteArray()); | ||
132 | if (profileOpt.isPresent()) { | 138 | if (profileOpt.isPresent()) { |
133 | profile = profileOpt.get(); | 139 | profile = profileOpt.get(); |
134 | TenantProfile existingProfile = profiles.get(profile.getId()); | 140 | TenantProfile existingProfile = profiles.get(profile.getId()); |
@@ -140,9 +146,11 @@ public class DefaultTransportTenantProfileCache implements TransportTenantProfil | @@ -140,9 +146,11 @@ public class DefaultTransportTenantProfileCache implements TransportTenantProfil | ||
140 | tenantProfileIds.computeIfAbsent(profile.getId(), id -> ConcurrentHashMap.newKeySet()).add(tenantId); | 146 | tenantProfileIds.computeIfAbsent(profile.getId(), id -> ConcurrentHashMap.newKeySet()).add(tenantId); |
141 | tenantIds.put(tenantId, profile.getId()); | 147 | tenantIds.put(tenantId, profile.getId()); |
142 | } else { | 148 | } else { |
143 | - log.warn("[{}] Can't decode tenant profile: {}", tenantId, routingInfo.getData()); | 149 | + log.warn("[{}] Can't decode tenant profile: {}", tenantId, entityProfileMsg.getData()); |
144 | throw new RuntimeException("Can't decode tenant profile!"); | 150 | throw new RuntimeException("Can't decode tenant profile!"); |
145 | } | 151 | } |
152 | + Optional<ApiUsageState> apiStateOpt = dataDecodingEncodingService.decode(entityProfileMsg.getApiState().toByteArray()); | ||
153 | + apiStateOpt.ifPresent(apiUsageState -> rateLimitService.update(tenantId, apiUsageState.isTransportEnabled())); | ||
146 | } | 154 | } |
147 | } finally { | 155 | } finally { |
148 | tenantProfileFetchLock.unlock(); | 156 | tenantProfileFetchLock.unlock(); |
@@ -445,6 +445,10 @@ public class ModelConstants { | @@ -445,6 +445,10 @@ public class ModelConstants { | ||
445 | public static final String API_USAGE_STATE_TENANT_ID_COLUMN = TENANT_ID_PROPERTY; | 445 | public static final String API_USAGE_STATE_TENANT_ID_COLUMN = TENANT_ID_PROPERTY; |
446 | public static final String API_USAGE_STATE_ENTITY_TYPE_COLUMN = ENTITY_TYPE_COLUMN; | 446 | public static final String API_USAGE_STATE_ENTITY_TYPE_COLUMN = ENTITY_TYPE_COLUMN; |
447 | public static final String API_USAGE_STATE_ENTITY_ID_COLUMN = ENTITY_ID_COLUMN; | 447 | public static final String API_USAGE_STATE_ENTITY_ID_COLUMN = ENTITY_ID_COLUMN; |
448 | + public static final String API_USAGE_STATE_TRANSPORT_ENABLED_COLUMN = "transport_enabled"; | ||
449 | + public static final String API_USAGE_STATE_DB_STORAGE_ENABLED_COLUMN = "db_storage_enabled"; | ||
450 | + public static final String API_USAGE_STATE_RE_EXEC_ENABLED_COLUMN = "re_exec_enabled"; | ||
451 | + public static final String API_USAGE_STATE_JS_EXEC_ENABLED_COLUMN = "js_exec_enabled"; | ||
448 | 452 | ||
449 | /** | 453 | /** |
450 | * Cassandra attributes and timeseries constants. | 454 | * Cassandra attributes and timeseries constants. |
@@ -17,6 +17,8 @@ package org.thingsboard.server.dao.model.sql; | @@ -17,6 +17,8 @@ package org.thingsboard.server.dao.model.sql; | ||
17 | 17 | ||
18 | import lombok.Data; | 18 | import lombok.Data; |
19 | import lombok.EqualsAndHashCode; | 19 | import lombok.EqualsAndHashCode; |
20 | +import lombok.Getter; | ||
21 | +import lombok.Setter; | ||
20 | import org.hibernate.annotations.TypeDef; | 22 | import org.hibernate.annotations.TypeDef; |
21 | import org.thingsboard.server.common.data.ApiUsageState; | 23 | import org.thingsboard.server.common.data.ApiUsageState; |
22 | import org.thingsboard.server.common.data.id.EntityIdFactory; | 24 | import org.thingsboard.server.common.data.id.EntityIdFactory; |
@@ -51,6 +53,15 @@ public class ApiUsageStateEntity extends BaseSqlEntity<ApiUsageState> implements | @@ -51,6 +53,15 @@ public class ApiUsageStateEntity extends BaseSqlEntity<ApiUsageState> implements | ||
51 | @Column(name = ModelConstants.API_USAGE_STATE_ENTITY_ID_COLUMN) | 53 | @Column(name = ModelConstants.API_USAGE_STATE_ENTITY_ID_COLUMN) |
52 | private UUID entityId; | 54 | private UUID entityId; |
53 | 55 | ||
56 | + @Column(name = ModelConstants.API_USAGE_STATE_TRANSPORT_ENABLED_COLUMN) | ||
57 | + private boolean transportEnabled = true; | ||
58 | + @Column(name = ModelConstants.API_USAGE_STATE_DB_STORAGE_ENABLED_COLUMN) | ||
59 | + private boolean dbStorageEnabled = true; | ||
60 | + @Column(name = ModelConstants.API_USAGE_STATE_RE_EXEC_ENABLED_COLUMN) | ||
61 | + private boolean reExecEnabled = true; | ||
62 | + @Column(name = ModelConstants.API_USAGE_STATE_JS_EXEC_ENABLED_COLUMN) | ||
63 | + private boolean jsExecEnabled = true; | ||
64 | + | ||
54 | public ApiUsageStateEntity() { | 65 | public ApiUsageStateEntity() { |
55 | } | 66 | } |
56 | 67 | ||
@@ -66,6 +77,10 @@ public class ApiUsageStateEntity extends BaseSqlEntity<ApiUsageState> implements | @@ -66,6 +77,10 @@ public class ApiUsageStateEntity extends BaseSqlEntity<ApiUsageState> implements | ||
66 | this.entityType = ur.getEntityId().getEntityType().name(); | 77 | this.entityType = ur.getEntityId().getEntityType().name(); |
67 | this.entityId = ur.getEntityId().getId(); | 78 | this.entityId = ur.getEntityId().getId(); |
68 | } | 79 | } |
80 | + this.transportEnabled = ur.isTransportEnabled(); | ||
81 | + this.dbStorageEnabled = ur.isDbStorageEnabled(); | ||
82 | + this.reExecEnabled = ur.isReExecEnabled(); | ||
83 | + this.jsExecEnabled = ur.isJsExecEnabled(); | ||
69 | } | 84 | } |
70 | 85 | ||
71 | @Override | 86 | @Override |
@@ -78,6 +93,10 @@ public class ApiUsageStateEntity extends BaseSqlEntity<ApiUsageState> implements | @@ -78,6 +93,10 @@ public class ApiUsageStateEntity extends BaseSqlEntity<ApiUsageState> implements | ||
78 | if (entityId != null) { | 93 | if (entityId != null) { |
79 | ur.setEntityId(EntityIdFactory.getByTypeAndUuid(entityType, entityId)); | 94 | ur.setEntityId(EntityIdFactory.getByTypeAndUuid(entityType, entityId)); |
80 | } | 95 | } |
96 | + ur.setTransportEnabled(transportEnabled); | ||
97 | + ur.setDbStorageEnabled(dbStorageEnabled); | ||
98 | + ur.setReExecEnabled(reExecEnabled); | ||
99 | + ur.setJsExecEnabled(jsExecEnabled); | ||
81 | return ur; | 100 | return ur; |
82 | } | 101 | } |
83 | 102 |
dao/src/main/java/org/thingsboard/server/dao/usagerecord/ApiUsageStateServiceImpl.java
renamed from
dao/src/main/java/org/thingsboard/server/dao/usagerecord/ApiApiUsageStateServiceImpl.java
@@ -31,13 +31,13 @@ import static org.thingsboard.server.dao.service.Validator.validateId; | @@ -31,13 +31,13 @@ import static org.thingsboard.server.dao.service.Validator.validateId; | ||
31 | 31 | ||
32 | @Service | 32 | @Service |
33 | @Slf4j | 33 | @Slf4j |
34 | -public class ApiApiUsageStateServiceImpl extends AbstractEntityService implements ApiUsageStateService { | 34 | +public class ApiUsageStateServiceImpl extends AbstractEntityService implements ApiUsageStateService { |
35 | public static final String INCORRECT_TENANT_ID = "Incorrect tenantId "; | 35 | public static final String INCORRECT_TENANT_ID = "Incorrect tenantId "; |
36 | 36 | ||
37 | private final ApiUsageStateDao apiUsageStateDao; | 37 | private final ApiUsageStateDao apiUsageStateDao; |
38 | private final TenantDao tenantDao; | 38 | private final TenantDao tenantDao; |
39 | 39 | ||
40 | - public ApiApiUsageStateServiceImpl(TenantDao tenantDao, ApiUsageStateDao apiUsageStateDao) { | 40 | + public ApiUsageStateServiceImpl(TenantDao tenantDao, ApiUsageStateDao apiUsageStateDao) { |
41 | this.tenantDao = tenantDao; | 41 | this.tenantDao = tenantDao; |
42 | this.apiUsageStateDao = apiUsageStateDao; | 42 | this.apiUsageStateDao = apiUsageStateDao; |
43 | } | 43 | } |
@@ -411,5 +411,9 @@ CREATE TABLE IF NOT EXISTS api_usage_state ( | @@ -411,5 +411,9 @@ CREATE TABLE IF NOT EXISTS api_usage_state ( | ||
411 | tenant_id uuid, | 411 | tenant_id uuid, |
412 | entity_type varchar(32), | 412 | entity_type varchar(32), |
413 | entity_id uuid, | 413 | entity_id uuid, |
414 | + transport_enabled boolean, | ||
415 | + db_storage_enabled boolean, | ||
416 | + re_exec_enabled boolean, | ||
417 | + js_exec_enabled boolean, | ||
414 | CONSTRAINT api_usage_state_unq_key UNIQUE (tenant_id, entity_id) | 418 | CONSTRAINT api_usage_state_unq_key UNIQUE (tenant_id, entity_id) |
415 | ); | 419 | ); |
@@ -437,6 +437,10 @@ CREATE TABLE IF NOT EXISTS api_usage_state ( | @@ -437,6 +437,10 @@ CREATE TABLE IF NOT EXISTS api_usage_state ( | ||
437 | tenant_id uuid, | 437 | tenant_id uuid, |
438 | entity_type varchar(32), | 438 | entity_type varchar(32), |
439 | entity_id uuid, | 439 | entity_id uuid, |
440 | + transport_enabled boolean, | ||
441 | + db_storage_enabled boolean, | ||
442 | + re_exec_enabled boolean, | ||
443 | + js_exec_enabled boolean, | ||
440 | CONSTRAINT api_usage_state_unq_key UNIQUE (tenant_id, entity_id) | 444 | CONSTRAINT api_usage_state_unq_key UNIQUE (tenant_id, entity_id) |
441 | ); | 445 | ); |
442 | 446 |
@@ -48,4 +48,17 @@ public abstract class BaseApiUsageStateServiceTest extends AbstractServiceTest { | @@ -48,4 +48,17 @@ public abstract class BaseApiUsageStateServiceTest extends AbstractServiceTest { | ||
48 | Assert.assertNotNull(apiUsageState); | 48 | Assert.assertNotNull(apiUsageState); |
49 | } | 49 | } |
50 | 50 | ||
51 | + @Test | ||
52 | + public void testUpdateApiUsageState(){ | ||
53 | + ApiUsageState apiUsageState = apiUsageStateService.findTenantApiUsageState(tenantId); | ||
54 | + Assert.assertNotNull(apiUsageState); | ||
55 | + Assert.assertTrue(apiUsageState.isTransportEnabled()); | ||
56 | + apiUsageState.setTransportEnabled(false); | ||
57 | + apiUsageState = apiUsageStateService.update(apiUsageState); | ||
58 | + Assert.assertNotNull(apiUsageState); | ||
59 | + apiUsageState = apiUsageStateService.findTenantApiUsageState(tenantId); | ||
60 | + Assert.assertNotNull(apiUsageState); | ||
61 | + Assert.assertFalse(apiUsageState.isTransportEnabled()); | ||
62 | + } | ||
63 | + | ||
51 | } | 64 | } |