|
1 | 1 | package com.launchdarkly.sdk.server.ai;
|
2 | 2 |
|
| 3 | +import static java.util.Arrays.binarySearch; |
| 4 | + |
3 | 5 | import java.util.ArrayList;
|
4 | 6 | import java.util.HashMap;
|
5 | 7 | import java.util.List;
|
@@ -43,113 +45,114 @@ public LDAiClient(LDClientInterface client) {
|
43 | 45 |
|
44 | 46 | /**
|
45 | 47 | * Method to convert the JSON variable into the AiConfig object
|
| 48 | + * |
| 49 | + * If the parsing failed, the code will log an error and |
| 50 | + * return a well formed but with nullable value nulled and disabled AIConfig |
| 51 | + * |
| 52 | + * Doing all the error checks, so if somehow LD backend return incorrect value types, there is logging |
| 53 | + * This also opens up the possibility of allowing customer to build this using a JSON string in the future |
46 | 54 | *
|
47 | 55 | * @param value
|
48 | 56 | * @param key
|
49 | 57 | */
|
50 | 58 | protected AiConfig parseAiConfig(LDValue value, String key) {
|
51 |
| - AiConfig result = new AiConfig(); |
52 |
| - |
53 |
| - try { |
54 |
| - // Verify the whole value is a JSON object |
55 |
| - if (value == null || value.getType() != LDValueType.OBJECT) { |
56 |
| - throw new AiConfigParseException("Input to parseAiConfig must be a JSON object"); |
57 |
| - } |
58 |
| - |
59 |
| - // Convert the _meta JSON object into Meta |
60 |
| - LDValue valueMeta = value.get("_ldMeta"); |
61 |
| - if (valueMeta == LDValue.ofNull() || valueMeta.getType() != LDValueType.OBJECT) { |
62 |
| - throw new AiConfigParseException("_ldMeta must be a JSON object"); |
63 |
| - } |
| 59 | + boolean enabled = false; |
64 | 60 |
|
65 |
| - // Create Meta using constructor |
66 |
| - Meta meta = new Meta( |
67 |
| - ldValueNullCheck(valueMeta.get("variationKey")).stringValue(), |
68 |
| - Optional.of(valueMeta.get("version").intValue()), |
69 |
| - ldValueNullCheck(valueMeta.get("enabled")).booleanValue() |
70 |
| - ); |
71 |
| - result.setMeta(meta); |
72 |
| - |
73 |
| - // Convert the optional messages from an JSON array of JSON objects into Message |
74 |
| - // Q: Does it even make sense to have 0 messages? |
75 |
| - LDValue valueMessages = value.get("messages"); |
76 |
| - if (valueMeta == LDValue.ofNull() || valueMessages.getType() != LDValueType.ARRAY) { |
77 |
| - throw new AiConfigParseException("messages must be a JSON array"); |
78 |
| - } |
79 |
| - |
80 |
| - List<Message> messages = new ArrayList<Message>(); |
81 |
| - valueMessages.valuesAs(new Message.MessageConverter()); |
82 |
| - for (Message message : valueMessages.valuesAs(new Message.MessageConverter())) { |
83 |
| - messages.add(message); |
84 |
| - } |
85 |
| - result.setMessages(messages); |
| 61 | + // Verify the whole value is a JSON object |
| 62 | + if(!checkValueWithFailureLogging(value, LDValueType.OBJECT, logger, "Input to parseAiConfig must be a JSON object")) { |
| 63 | + return new AiConfig(enabled, null, null, null, null); |
| 64 | + } |
86 | 65 |
|
87 |
| - // Convert the optional model from an JSON object of with parameters and custom |
88 |
| - // into Model |
89 |
| - LDValue valueModel = value.get("model"); |
90 |
| - if (valueModel == LDValue.ofNull() || valueModel.getType() != LDValueType.OBJECT) { |
91 |
| - throw new AiConfigParseException("model must be a JSON object"); |
92 |
| - } |
| 66 | + // Convert the _meta JSON object into Meta |
| 67 | + LDValue valueMeta = value.get("_ldMeta"); |
| 68 | + if (!checkValueWithFailureLogging(valueMeta, LDValueType.OBJECT, logger, "_ldMeta must be a JSON object")) { |
| 69 | + // Q: If we can't read _meta, enabled by spec would be defaulted to false. Does it even matter the rest of the values? |
| 70 | + return new AiConfig(enabled, null, null, null, null); |
| 71 | + } |
93 | 72 |
|
94 |
| - // Prepare parameters and custom maps for Model |
95 |
| - String modelName = ldValueNullCheck(valueModel.get("name")).stringValue(); |
96 |
| - HashMap<String, LDValue> parameters = null; |
97 |
| - HashMap<String, LDValue> custom = null; |
| 73 | + // The booleanValue will get false if that value something that we are not expecting, which is good |
| 74 | + enabled = valueMeta.get("enabled").booleanValue(); |
98 | 75 |
|
99 |
| - LDValue valueParameters = valueModel.get("parameters"); |
100 |
| - if (valueParameters.getType() != LDValueType.NULL) { |
101 |
| - if (valueParameters.getType() != LDValueType.OBJECT) { |
102 |
| - throw new AiConfigParseException("non-null parameters must be a JSON object"); |
| 76 | + String variationKey = null; |
| 77 | + if (checkValueWithFailureLogging(valueMeta.get("variationKey"), LDValueType.STRING, logger, "variationKey should be a string")) { |
| 78 | + variationKey = valueMeta.get("variationKey").stringValue(); |
| 79 | + } |
| 80 | + // Create Meta using constructor |
| 81 | + Meta meta = new Meta( |
| 82 | + variationKey, |
| 83 | + Optional.of(valueMeta.get("version").intValue()) |
| 84 | + ); |
| 85 | + |
| 86 | + // Convert the optional model from an JSON object of with parameters and custom |
| 87 | + // into Model |
| 88 | + Model model = null; |
| 89 | + |
| 90 | + LDValue valueModel = value.get("model"); |
| 91 | + if (checkValueWithFailureLogging(valueModel, LDValueType.OBJECT, logger, "model if exists must be a JSON object")) { |
| 92 | + if (checkValueWithFailureLogging(valueModel.get("name"), LDValueType.STRING, logger, "model name must be a string and is required")) { |
| 93 | + String modelName = valueModel.get("name").stringValue(); |
| 94 | + |
| 95 | + // Prepare parameters and custom maps for Model |
| 96 | + HashMap<String, LDValue> parameters = null; |
| 97 | + HashMap<String, LDValue> custom = null; |
| 98 | + |
| 99 | + LDValue valueParameters = valueModel.get("parameters"); |
| 100 | + if (checkValueWithFailureLogging(valueParameters, LDValueType.OBJECT, logger, "non-null parameters must be a JSON object")) { |
| 101 | + parameters = new HashMap<>(); |
| 102 | + for (String k : valueParameters.keys()) { |
| 103 | + parameters.put(k, valueParameters.get(k)); |
| 104 | + } |
103 | 105 | }
|
104 |
| - |
105 |
| - parameters = new HashMap<>(); |
106 |
| - for (String k : valueParameters.keys()) { |
107 |
| - parameters.put(k, valueParameters.get(k)); |
| 106 | + |
| 107 | + LDValue valueCustom = valueModel.get("custom"); |
| 108 | + if (checkValueWithFailureLogging(valueCustom, LDValueType.OBJECT, logger, "non-null custom must be a JSON object")) { |
| 109 | + |
| 110 | + custom = new HashMap<>(); |
| 111 | + for (String k : valueCustom.keys()) { |
| 112 | + custom.put(k, valueCustom.get(k)); |
| 113 | + } |
108 | 114 | }
|
| 115 | + |
| 116 | + model = new Model(modelName, parameters, custom); |
109 | 117 | }
|
| 118 | + } |
110 | 119 |
|
111 |
| - LDValue valueCustom = valueModel.get("custom"); |
112 |
| - if (valueCustom.getType() != LDValueType.NULL) { |
113 |
| - if (valueCustom.getType() != LDValueType.OBJECT) { |
114 |
| - throw new AiConfigParseException("non-null custom must be a JSON object"); |
115 |
| - } |
| 120 | + // Convert the optional messages from an JSON array of JSON objects into Message |
| 121 | + // Q: Does it even make sense to have 0 messages? |
| 122 | + List<Message> messages = null; |
116 | 123 |
|
117 |
| - custom = new HashMap<>(); |
118 |
| - for (String k : valueCustom.keys()) { |
119 |
| - custom.put(k, valueCustom.get(k)); |
120 |
| - } |
| 124 | + LDValue valueMessages = value.get("messages"); |
| 125 | + if (checkValueWithFailureLogging(valueMessages, LDValueType.ARRAY, logger, "messages if exists must be a JSON array")) { |
| 126 | + messages = new ArrayList<Message>(); |
| 127 | + valueMessages.valuesAs(new Message.MessageConverter()); |
| 128 | + for (Message message : valueMessages.valuesAs(new Message.MessageConverter())) { |
| 129 | + messages.add(message); |
121 | 130 | }
|
122 |
| - |
123 |
| - // Create Model using constructor |
124 |
| - Model model = new Model(modelName, parameters, custom); |
125 |
| - result.setModel(model); |
126 |
| - |
127 |
| - // Convert the optional provider from an JSON object of with name into Provider |
128 |
| - LDValue valueProvider = value.get("provider"); |
129 |
| - if (valueProvider.getType() != LDValueType.NULL) { |
130 |
| - if (valueProvider.getType() != LDValueType.OBJECT) { |
131 |
| - throw new AiConfigParseException("non-null provider must be a JSON object"); |
132 |
| - } |
| 131 | + } |
133 | 132 |
|
134 |
| - Provider provider = new Provider(ldValueNullCheck(valueProvider.get("name")).stringValue()); |
135 |
| - result.setProvider(provider); |
136 |
| - } else { |
137 |
| - // Provider is optional - we can just set null and proceed |
138 |
| - result.setProvider(null); |
| 133 | + // Convert the optional provider from an JSON object of with name into Provider |
| 134 | + LDValue valueProvider = value.get("provider"); |
| 135 | + String providerName = null; |
| 136 | + |
| 137 | + if(checkValueWithFailureLogging(valueProvider, LDValueType.OBJECT, logger, "non-null provider must be a JSON object")) { |
| 138 | + if(checkValueWithFailureLogging(valueProvider.get("name"), LDValueType.STRING, logger, "provider name must be a String")) { |
| 139 | + providerName = valueProvider.get("name").stringValue(); |
139 | 140 | }
|
140 |
| - } catch (AiConfigParseException e) { |
141 |
| - // logger.error(e.getMessage()); |
142 |
| - return null; |
143 | 141 | }
|
144 | 142 |
|
145 |
| - return result; |
| 143 | + Provider provider = new Provider(providerName); |
| 144 | + |
| 145 | + return new AiConfig(enabled, meta, model, messages, provider); |
146 | 146 | }
|
147 | 147 |
|
148 |
| - protected <T> T ldValueNullCheck(T ldValue) throws AiConfigParseException { |
149 |
| - if (ldValue == LDValue.ofNull()) { |
150 |
| - throw new AiConfigParseException("Unexpected Null value for non-optional field"); |
| 148 | + protected boolean checkValueWithFailureLogging(LDValue ldValue, LDValueType expectedType, LDLogger logger, String message) { |
| 149 | + if (ldValue.getType() != expectedType) { |
| 150 | + if (logger != null) { |
| 151 | + logger.error(message); |
| 152 | + } |
| 153 | + return false; |
151 | 154 | }
|
152 |
| - return ldValue; |
| 155 | + return true; |
153 | 156 | }
|
154 | 157 |
|
155 | 158 | class AiConfigParseException extends Exception {
|
|
0 commit comments