Skip to content

Commit ed4d82a

Browse files
committed
fix: support multi-resource yaml files
* added support for multi-resource yaml files * added tests for KubernetesTypeInfo Signed-off-by: Andre Dietisheim <[email protected]>
1 parent a0e326d commit ed4d82a

File tree

5 files changed

+865
-71
lines changed

5 files changed

+865
-71
lines changed

src/main/java/com/redhat/devtools/intellij/common/validation/KubernetesResourceInfo.java

+38-26
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@
1818
import com.intellij.psi.PsiFile;
1919
import com.intellij.psi.PsiNamedElement;
2020
import org.jetbrains.annotations.NotNull;
21+
import org.jetbrains.annotations.Nullable;
22+
import org.jetbrains.yaml.psi.YAMLDocument;
2123
import org.jetbrains.yaml.psi.YAMLFile;
2224
import org.jetbrains.yaml.psi.YAMLKeyValue;
2325
import org.jetbrains.yaml.psi.YAMLValue;
@@ -34,39 +36,46 @@ public class KubernetesResourceInfo {
3436
private String namespace;
3537
private KubernetesTypeInfo typeInfo;
3638

37-
public KubernetesResourceInfo(String name, String namespace, KubernetesTypeInfo info) {
39+
KubernetesResourceInfo(String name, String namespace, KubernetesTypeInfo info) {
3840
this.name = name;
3941
this.namespace = namespace;
4042
this.typeInfo = info;
4143
}
4244

43-
public KubernetesResourceInfo() {
44-
}
45+
private KubernetesResourceInfo() {}
4546

46-
public static KubernetesResourceInfo extractMeta(PsiFile file) {
47+
public static KubernetesResourceInfo create(PsiFile file) {
4748
KubernetesResourceInfo resourceInfo = new KubernetesResourceInfo();
4849
if (file instanceof JsonFile) {
49-
extractJsonMeta((JsonFile) file, resourceInfo);
50+
create((JsonFile) file, resourceInfo);
5051
} else if (file instanceof YAMLFile) {
51-
extractYAMLMeta((YAMLFile) file, resourceInfo);
52+
create((YAMLFile) file, resourceInfo);
5253
}
53-
resourceInfo.setTypeInfo(KubernetesTypeInfo.extractMeta(file));
54+
resourceInfo.setTypeInfo(KubernetesTypeInfo.create(file));
5455
return resourceInfo;
5556
}
5657

57-
private static void extractJsonMeta(JsonFile file, KubernetesResourceInfo resourceInfo) {
58+
private static void create(JsonFile file, KubernetesResourceInfo resourceInfo) {
5859
JsonValue content = file.getTopLevelValue();
5960
if (content == null) {
6061
return;
6162
}
6263
content.acceptChildren(new ResourceVisitor(resourceInfo));
6364
}
6465

65-
private static void extractYAMLMeta(YAMLFile file, KubernetesResourceInfo resourceInfo) {
66+
private static void create(YAMLFile file, KubernetesResourceInfo resourceInfo) {
6667
if (file.getDocuments().isEmpty()) {
6768
return;
6869
}
69-
YAMLValue content = file.getDocuments().get(0).getTopLevelValue();
70+
// only use the first document in the file
71+
create(file.getDocuments().get(0), resourceInfo);
72+
}
73+
74+
private static void create(YAMLDocument document, KubernetesResourceInfo resourceInfo) {
75+
if (document == null) {
76+
return;
77+
}
78+
YAMLValue content = document.getTopLevelValue();
7079
if (content == null) {
7180
return;
7281
}
@@ -89,11 +98,7 @@ public void setNamespace(String namespace) {
8998
this.namespace = namespace;
9099
}
91100

92-
public KubernetesTypeInfo getTypeInfo() {
93-
return typeInfo;
94-
}
95-
96-
public void setTypeInfo(KubernetesTypeInfo typeInfo) {
101+
private void setTypeInfo(KubernetesTypeInfo typeInfo) {
97102
this.typeInfo = typeInfo;
98103
}
99104

@@ -121,34 +126,41 @@ private static class ResourceVisitor extends PsiElementVisitor {
121126

122127
private final KubernetesResourceInfo info;
123128

124-
public ResourceVisitor(KubernetesResourceInfo info) {
129+
private ResourceVisitor(KubernetesResourceInfo info) {
125130
this.info = info;
126131
}
127132

128133
@Override
129134
public void visitElement(@NotNull PsiElement element) {
130-
if (!(element instanceof PsiNamedElement)) {
135+
if (!(element instanceof PsiNamedElement namedElement)) {
131136
return;
132137
}
133-
PsiNamedElement namedElement = (PsiNamedElement) element;
134138
if (KEY_METADATA.equals(namedElement.getName())) {
135-
if (namedElement instanceof JsonProperty) {
136-
((JsonProperty) namedElement).getValue().acceptChildren(visitMetadata());
137-
} else if (namedElement instanceof YAMLKeyValue) {
138-
((YAMLKeyValue) namedElement).getValue().acceptChildren(visitMetadata());
139+
var value = getValue(namedElement);
140+
if (value != null) {
141+
value.acceptChildren(visitMetadata());
139142
}
140143
}
141144
}
142145

146+
private @Nullable PsiElement getValue(PsiNamedElement namedElement) {
147+
if (namedElement instanceof JsonProperty) {
148+
return ((JsonProperty) namedElement).getValue();
149+
} else if (namedElement instanceof YAMLKeyValue) {
150+
return ((YAMLKeyValue) namedElement).getValue();
151+
} else {
152+
return null;
153+
}
154+
}
155+
143156
@NotNull
144157
private PsiElementVisitor visitMetadata() {
145158
return new PsiElementVisitor() {
146159
@Override
147-
public void visitElement(PsiElement element) {
148-
if (!(element instanceof PsiNamedElement)) {
160+
public void visitElement(@NotNull PsiElement element) {
161+
if (!(element instanceof PsiNamedElement namedElement)) {
149162
return;
150163
}
151-
PsiNamedElement namedElement = (PsiNamedElement) element;
152164
if (KEY_NAME.equals(namedElement.getName())) {
153165
setName(namedElement);
154166
} else if (KEY_NAMESPACE.equals(namedElement.getName())) {
@@ -177,7 +189,7 @@ private String valueOrNull(JsonProperty property) {
177189
}
178190

179191
private String valueOrNull(YAMLKeyValue key) {
180-
return key.getValueText() != null ? key.getValueText() : null;
192+
return key.getValueText();
181193
}
182194

183195
};

src/main/java/com/redhat/devtools/intellij/common/validation/KubernetesTypeInfo.java

+123-44
Original file line numberDiff line numberDiff line change
@@ -13,18 +13,26 @@
1313
import com.intellij.json.psi.JsonFile;
1414
import com.intellij.json.psi.JsonProperty;
1515
import com.intellij.json.psi.JsonValue;
16+
import com.intellij.openapi.util.text.StringUtil;
1617
import com.intellij.psi.PsiElement;
1718
import com.intellij.psi.PsiElementVisitor;
1819
import com.intellij.psi.PsiFile;
19-
import java.util.Objects;
2020
import org.jetbrains.annotations.NotNull;
21+
import org.jetbrains.yaml.psi.YAMLDocument;
2122
import org.jetbrains.yaml.psi.YAMLFile;
2223
import org.jetbrains.yaml.psi.YAMLKeyValue;
2324
import org.jetbrains.yaml.psi.YAMLValue;
2425

26+
import java.util.List;
27+
import java.util.Objects;
28+
2529
public class KubernetesTypeInfo {
30+
31+
private static final String KEY_API_VERSION = "apiVersion";
32+
private static final String KEY_KIND = "kind";
33+
2634
private String apiGroup = "";
27-
private String kind= "";
35+
private String kind = "";
2836

2937
public KubernetesTypeInfo(String apiGroup, String kind) {
3038
this.apiGroup = apiGroup;
@@ -33,55 +41,72 @@ public KubernetesTypeInfo(String apiGroup, String kind) {
3341

3442
public KubernetesTypeInfo() {}
3543

36-
public static KubernetesTypeInfo extractMeta(PsiFile file) {
37-
KubernetesTypeInfo info = new KubernetesTypeInfo();
44+
public static KubernetesTypeInfo create(PsiFile file) {
3845
if (file instanceof JsonFile) {
39-
extractJsonMeta((JsonFile) file, info);
46+
return create((JsonFile) file);
4047
} else if (file instanceof YAMLFile) {
41-
extractYAMLMeta((YAMLFile) file, info);
48+
return create((YAMLFile) file);
49+
} else {
50+
return null;
4251
}
43-
return info;
4452
}
4553

46-
private static void extractJsonMeta(JsonFile file, KubernetesTypeInfo info) {
47-
JsonValue content = file.getTopLevelValue();
54+
private static KubernetesTypeInfo create(JsonFile file) {
55+
var collector = new JsonKubernetesTypeInfoVisitor();
56+
final JsonValue content = file.getTopLevelValue();
4857
if (content != null) {
49-
content.acceptChildren(new PsiElementVisitor() {
50-
@Override
51-
public void visitElement(@NotNull PsiElement element) {
52-
if (element instanceof JsonProperty) {
53-
JsonProperty property = (JsonProperty) element;
54-
if (property.getName().equals("apiVersion")) {
55-
info.setApiGroup(property.getValue().getText());
56-
} else if (property.getName().equals("kind")) {
57-
info.setKind(property.getValue().getText());
58-
}
59-
}
60-
}
61-
});
58+
content.acceptChildren(collector);
6259
}
60+
return collector.getKubernetesTypeInfo();
6361
}
6462

65-
private static void extractYAMLMeta(YAMLFile file, KubernetesTypeInfo info) {
66-
if (!file.getDocuments().isEmpty()) {
67-
YAMLValue content = file.getDocuments().get(0).getTopLevelValue();
68-
if (content != null) {
69-
content.acceptChildren(new PsiElementVisitor() {
70-
@Override
71-
public void visitElement(@NotNull PsiElement element) {
72-
if (element instanceof YAMLKeyValue) {
73-
YAMLKeyValue property = (YAMLKeyValue) element;
74-
if (property.getKeyText().equals("apiVersion")) {
75-
info.setApiGroup(property.getValueText());
76-
} else if (property.getKeyText().equals("kind")) {
77-
info.setKind(property.getValueText());
78-
}
79-
}
80-
}
81-
});
63+
/**
64+
* Extracts the k8s metadata of the first document in the given YAML file.
65+
*
66+
* @param file the yaml file to extract the k8s metadata of
67+
*
68+
* @return the k8s metadata of the first document in the given file
69+
*/
70+
private static KubernetesTypeInfo create(YAMLFile file) {
71+
if (file == null
72+
|| file.getDocuments().isEmpty()) {
73+
return null;
74+
}
75+
76+
// only use the first document in the file
77+
return create(file.getDocuments().get(0));
78+
}
8279

80+
/**
81+
* Creates a list of {@link KubernetesTypeInfo} for the given YAML file.
82+
* If the given file contains several documents then KubernetesTypeInfo's for each document will be created.
83+
* If there's only a single document only a single KuberenetesTypeInfo is created.
84+
* Returns {@code null} if the given file is {@code null} or empty.
85+
*
86+
* @param file the yaml file to create KubernetesTypeInfo's for
87+
*
88+
* @return KubernetesTypeInfos of all the documents in the given file
89+
*/
90+
static List<KubernetesTypeInfo> createTypes(YAMLFile file) {
91+
if (file == null
92+
|| file.getDocuments().isEmpty()) {
93+
return null;
94+
}
95+
96+
return file.getDocuments().stream()
97+
.map(KubernetesTypeInfo::create)
98+
.toList();
99+
}
100+
101+
public static KubernetesTypeInfo create(YAMLDocument document) {
102+
final KubernetesTypeInfoVisitor collector = new YAMLKubernetesTypeInfoVisitor();
103+
if (document != null) {
104+
final YAMLValue content = document.getTopLevelValue();
105+
if (content != null) {
106+
content.acceptChildren(collector);
83107
}
84108
}
109+
return collector.getKubernetesTypeInfo();
85110
}
86111

87112
public String getApiGroup() {
@@ -121,11 +146,65 @@ public String toString() {
121146

122147
public static KubernetesTypeInfo fromFileName(String filename) {
123148
int index = filename.indexOf('_');
124-
String apiGroup = (index != (-1))?filename.substring(0, index):"";
125-
String kind = (index != (-1))?filename.substring(index + 1):filename;
126-
index = kind.lastIndexOf('.');
127-
kind = (index != (-1))?kind.substring(0, index):kind;
128-
return new KubernetesTypeInfo(apiGroup, kind);
149+
String apiGroup = (index != (-1)) ? filename.substring(0, index) : "";
150+
String kind = (index != (-1)) ? filename.substring(index + 1) : filename;
151+
index = kind.lastIndexOf('.');
152+
kind = (index != (-1)) ? kind.substring(0, index) : kind;
153+
return new KubernetesTypeInfo(apiGroup, kind);
154+
}
155+
156+
private static abstract class KubernetesTypeInfoVisitor extends PsiElementVisitor {
129157

158+
private KubernetesTypeInfo info = null;
159+
160+
protected void setApiGroup(String apiGroup) {
161+
existingOrCreate().setApiGroup(apiGroup);
130162
}
163+
164+
protected void setKind(String kind) {
165+
existingOrCreate().setKind(kind);
166+
}
167+
168+
private KubernetesTypeInfo existingOrCreate() {
169+
if (info == null) {
170+
this.info = new KubernetesTypeInfo();
171+
}
172+
return info;
173+
}
174+
175+
public KubernetesTypeInfo getKubernetesTypeInfo() {
176+
return info;
177+
}
178+
}
179+
180+
static class YAMLKubernetesTypeInfoVisitor extends KubernetesTypeInfoVisitor {
181+
@Override
182+
public void visitElement(@NotNull PsiElement element) {
183+
if (element instanceof YAMLKeyValue property) {
184+
String value = StringUtil.unquoteString(property.getValueText());
185+
if (property.getKeyText().equals(KEY_API_VERSION)) {
186+
setApiGroup(value);
187+
} else if (property.getKeyText().equals(KEY_KIND)) {
188+
setKind(value);
189+
}
190+
}
191+
}
192+
}
193+
194+
static class JsonKubernetesTypeInfoVisitor extends KubernetesTypeInfoVisitor {
195+
@Override
196+
public void visitElement(@NotNull PsiElement element) {
197+
if (element instanceof JsonProperty property
198+
&& property.getValue() != null
199+
&& property.getValue().getText() != null) {
200+
String value = StringUtil.unquoteString(property.getValue().getText());
201+
if (property.getName().equals(KEY_API_VERSION)) {
202+
setApiGroup(value);
203+
} else if (property.getName().equals(KEY_KIND)) {
204+
setKind(value);
205+
}
206+
}
207+
}
208+
}
209+
131210
}

src/main/java/com/redhat/devtools/intellij/common/validation/SchemaProvider.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ public boolean isAvailable(@NotNull VirtualFile file) {
4141
return ApplicationManager.getApplication().runReadAction((Computable<Boolean>) () -> {
4242
PsiFile psiFile = PsiManager.getInstance(project).findFile(file);
4343
if (psiFile != null) {
44-
KubernetesTypeInfo fileInfo = KubernetesTypeInfo.extractMeta(psiFile);
44+
KubernetesTypeInfo fileInfo = KubernetesTypeInfo.create(psiFile);
4545
return info.equals(fileInfo);
4646
}
4747
return false;

0 commit comments

Comments
 (0)