Skip to content

Commit 1e9b7d2

Browse files
committedMay 7, 2024··
feat: Grammar support and custom theme
1 parent dc71386 commit 1e9b7d2

27 files changed

+463
-92
lines changed
 

‎.github/CODEOWNERS

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
* @openfga/dx
2+
README.md @openfga/product @openfga/community @openfga/dx

‎.github/dependabot.yml

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
# Dependabot configuration:
2+
# https://docs.github.com/en/free-pro-team@latest/github/administering-a-repository/configuration-options-for-dependency-updates
3+
4+
version: 2
5+
updates:
6+
# Maintain dependencies for Gradle dependencies
7+
- package-ecosystem: "gradle"
8+
directory: "/"
9+
schedule:
10+
interval: "weekly"
11+
# Maintain dependencies for GitHub Actions
12+
- package-ecosystem: "github-actions"
13+
directory: "/"
14+
schedule:
15+
interval: "weekly"

‎README.md

+59-4
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,68 @@
1-
# openfga4intellij
1+
# OpenFGA plugin for JetBrain IDEs
22

3+
This is the official IntelliJ plugin for [OpenFGA](https://openfga.dev/).
34

4-
This is an unofficial IntelliJ plugin for [OpenFGA](https://openfga.dev/) support.
5+
[![License](https://img.shields.io/badge/License-Apache_2.0-blue.svg)](https://github.com/openfga/vscode-ext/blob/main/LICENSE)
6+
[![Join our community](https://img.shields.io/badge/slack-cncf_%23openfga-40abb8.svg?logo=slack)](https://openfga.dev/community)
7+
[![Twitter](https://img.shields.io/twitter/follow/openfga?color=%23179CF0&logo=twitter&style=flat-square "@openfga on Twitter")](https://twitter.com/openfga)
58

69

7-
## Features
10+
## About
811

9-
* _Partial_ DSL syntax support (associated with `.fga` and `.openfga` file extensions)
12+
[OpenFGA](https://openfga.dev) is an open source Fine-Grained Authorization solution inspired by [Google's Zanzibar paper](https://research.google/pubs/pub48190/). It was created by the FGA team at [Auth0](https://auth0.com) based on [Auth0 Fine-Grained Authorization (FGA)](https://fga.dev), available under [a permissive license (Apache-2)](https://github.com/openfga/rfcs/blob/main/LICENSE) and welcomes community contributions.
13+
14+
OpenFGA is designed to make it easy for application builders to model their permission layer, and to add and integrate fine-grained authorization into their applications. OpenFGA’s design is optimized for reliability and low latency at a high scale.
15+
16+
## Resources
17+
18+
- [OpenFGA Documentation](https://openfga.dev/docs)
19+
- [OpenFGA API Documentation](https://openfga.dev/api/service)
20+
- [OpenFGA on Twitter](https://twitter.com/openfga)
21+
- [OpenFGA Community](https://openfga.dev/community)
22+
- [Zanzibar Academy](https://zanzibar.academy)
23+
- [Google's Zanzibar Paper (2019)](https://research.google/pubs/pub48190/)
24+
25+
26+
## Installation
27+
28+
### Manual Installation
29+
30+
![manual_install.png](docs/manual_install.png)
31+
32+
## Usage
33+
34+
* IDE and Editor theme `OpenFGA Dark`
35+
![openfga_dark_theme.png](docs/openfga_dark_theme.png)
36+
* DSL syntax support (associated with `.fga` and `.openfga` file extensions)
37+
![syntax.png](docs/syntax.png)
38+
* DSL syntax injection for YAML store files (associated with `.fga.yaml` and `.openfga.yaml` file extensions)
39+
![store_syntax.png](docs/store_syntax.png)
1040
* Authorization model dsl file template
41+
![template.png](docs/template.png)
1142
* Authorization model dsl live templates
43+
![live_templates.png](docs/live_templates.png)
1244
* Generate json file from DSL (requires [OpenFGA CLI](https://github.com/openfga/cli) to be installed)
45+
![cli_setup.png](docs/cli_setup.png)
46+
![transform_json.png](docs/transform_json.png)
1347
* Configure servers in OpenFGA tool window
48+
![server_setup.png](docs/server_setup.png)
49+
50+
## Roadmap
51+
52+
A rough [roadmap](https://github.com/orgs/openfga/projects/1) for development priorities.
53+
54+
## Contributing
55+
56+
See the [DEVELOPMENT](./docs/DEVELOPMENT.md) and [CONTRIBUTING](https://github.com/openfga/.github/blob/main/CONTRIBUTING.md).
57+
58+
## Author
59+
60+
[OpenFGA](https://github.com/openfga)
61+
62+
## Acknowledgments
63+
64+
A special thanks to [Yann D'Isanto](https://github.com/le-yams) for the contribution of [their codebase](https://github.com/le-yams/openfga4intellij), which this project is built upon.
65+
66+
## License
67+
68+
This project is licensed under the Apache-2.0 license. See the [LICENSE](https://github.com/openfga/vscode-ext/blob/main/LICENSE) file for more info.

‎build.gradle.kts

+13-1
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ plugins {
44
id("org.jetbrains.kotlin.jvm") version "1.9.21"
55
id("org.jetbrains.intellij") version "1.16.1"
66
id("org.jetbrains.grammarkit") version "2022.3.2"
7+
8+
id("jacoco")
79
}
810

911
group = "com.github.le_yams"
@@ -17,6 +19,8 @@ dependencies {
1719
implementation("dev.openfga:openfga-sdk:0.4.1")
1820
implementation("org.dmfs:oauth2-essentials:0.22.0")
1921
implementation("org.dmfs:httpurlconnection-executor:1.21.3")
22+
23+
testImplementation("junit:junit:4.13.2")
2024
}
2125

2226

@@ -29,7 +33,7 @@ intellij {
2933
version.set("2023.3")
3034
type.set("IC") // Target IDE Platform
3135

32-
plugins.set(listOf(/* Plugin Dependencies */))
36+
plugins.set(listOf("org.intellij.intelliLang", "org.jetbrains.plugins.yaml"))
3337
}
3438

3539
grammarKit {
@@ -86,4 +90,12 @@ tasks {
8690
publishPlugin {
8791
token.set(System.getenv("PUBLISH_TOKEN"))
8892
}
93+
94+
test {
95+
finalizedBy(jacocoTestReport)
96+
}
97+
98+
jacocoTestReport {
99+
dependsOn(test)
100+
}
89101
}

‎docs/DEVELOPMENT.md

+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
# Development and Contributing to the OpenFGA VS Code Extension
2+
3+
Read the [OpenFGA Contribution Process](https://github.com/openfga/.github/blob/main/CONTRIBUTING.md) and the [OpenFGA Code of Conduct](https://github.com/openfga/.github/blob/main/CODE_OF_CONDUCT.md).
4+
5+
## Getting Started
6+
7+
### Run Build & tests
8+
9+
```./gradlew build```
10+
11+
## Distribution (Optional)
12+
13+
To generate an installable build of this plugin, you can do the following:
14+
15+
```./gradlew buildPlugin```
16+
17+
[Instructions for installation from disk](https://www.jetbrains.com/help/idea/managing-plugins.html#install_plugin_from_disk)

‎docs/cli_setup.png

543 KB
Loading

‎docs/live_templates.png

593 KB
Loading

‎docs/manual_install.png

317 KB
Loading

‎docs/openfga_dark_theme.png

658 KB
Loading

‎docs/server_setup.png

97.6 KB
Loading

‎docs/store_syntax.png

161 KB
Loading

‎docs/syntax.png

144 KB
Loading

‎docs/template.png

215 KB
Loading

‎docs/transform_json.png

457 KB
Loading

‎src/main/java/com/github/le_yams/openfga4intellij/OpenFGAColorSettingsPage.java

+2-2
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@ public class OpenFGAColorSettingsPage implements ColorSettingsPage {
1919
new AttributesDescriptor("Type identifier", OpenFGASyntaxHighlighter.TYPE_IDENTIFIER),
2020
new AttributesDescriptor("Relation name", OpenFGASyntaxHighlighter.RELATION_NAME),
2121
new AttributesDescriptor("Schema version", OpenFGASyntaxHighlighter.SCHEMA_VERSIONS),
22-
new AttributesDescriptor("Bad value", OpenFGASyntaxHighlighter.BAD_CHARACTER)
2322
};
2423

2524
@Nullable
@@ -37,7 +36,8 @@ public SyntaxHighlighter getHighlighter() {
3736
@NotNull
3837
@Override
3938
public String getDemoText() {
40-
return "model\n" +
39+
return "# Administrator model\n" +
40+
"model\n" +
4141
" schema 1.1\n" +
4242
"\n" +
4343
"type user\n" +

‎src/main/java/com/github/le_yams/openfga4intellij/OpenFGASyntaxHighlighter.java

+49-22
Original file line numberDiff line numberDiff line change
@@ -4,37 +4,50 @@
44
import com.github.le_yams.openfga4intellij.psi.OpenFGATypes;
55
import com.intellij.lexer.Lexer;
66
import com.intellij.openapi.editor.DefaultLanguageHighlighterColors;
7-
import com.intellij.openapi.editor.HighlighterColors;
87
import com.intellij.openapi.editor.colors.TextAttributesKey;
98
import com.intellij.openapi.fileTypes.SyntaxHighlighterBase;
10-
import com.intellij.psi.TokenType;
119
import com.intellij.psi.tree.IElementType;
1210
import org.jetbrains.annotations.NotNull;
1311

1412
import static com.intellij.openapi.editor.colors.TextAttributesKey.createTextAttributesKey;
1513

1614
public class OpenFGASyntaxHighlighter extends SyntaxHighlighterBase {
1715
public static final TextAttributesKey KEYWORD =
18-
createTextAttributesKey("KEYWORD", DefaultLanguageHighlighterColors.KEYWORD);
16+
createTextAttributesKey("OPENFGA_KEYWORD", DefaultLanguageHighlighterColors.KEYWORD);
1917
public static final TextAttributesKey SCHEMA_VERSIONS =
2018
createTextAttributesKey("OPENFGA_SCHEMA_VERSIONS", DefaultLanguageHighlighterColors.NUMBER);
19+
public static final TextAttributesKey MODULE_NAME =
20+
createTextAttributesKey("OPENFGA_MODULE_NAME", DefaultLanguageHighlighterColors.CLASS_NAME);
2121
public static final TextAttributesKey RELATION_NAME =
2222
createTextAttributesKey("OPENFGA_RELATION_NAME", DefaultLanguageHighlighterColors.INSTANCE_METHOD);
23+
public static final TextAttributesKey RELATION_REFERENCE =
24+
createTextAttributesKey("OPENFGA_RELATION_REFERENCE", DefaultLanguageHighlighterColors.INSTANCE_METHOD);
2325
public static final TextAttributesKey TYPE_IDENTIFIER =
2426
createTextAttributesKey("OPENFGA_TYPE_IDENTIFIER", DefaultLanguageHighlighterColors.CLASS_NAME);
2527
public static final TextAttributesKey TYPE_REFERENCE =
2628
createTextAttributesKey("OPENFGA_TYPE_REFERENCE", DefaultLanguageHighlighterColors.CLASS_REFERENCE);
29+
public static final TextAttributesKey CONDITION_IDENTIFIER =
30+
createTextAttributesKey("OPENFGA_CONDITION_IDENTIFIER", DefaultLanguageHighlighterColors.CLASS_NAME);
31+
public static final TextAttributesKey CONDITION_REFERENCE =
32+
createTextAttributesKey("OPENFGA_CONDITION_REFERENCE", DefaultLanguageHighlighterColors.CLASS_REFERENCE);
33+
public static final TextAttributesKey CONDITION_PARAM_IDENTIFIER =
34+
createTextAttributesKey("OPENFGA_CONDITION_PARAM_IDENTIFIER", DefaultLanguageHighlighterColors.INSTANCE_METHOD);
35+
public static final TextAttributesKey CONDITION_PARAM_TYPE =
36+
createTextAttributesKey("OPENFGA_CONDITION_PARAM_TYPE", DefaultLanguageHighlighterColors.PARAMETER);
2737
public static final TextAttributesKey COMMENT =
2838
createTextAttributesKey("OPENFGA_COMMENTS", DefaultLanguageHighlighterColors.LINE_COMMENT);
29-
public static final TextAttributesKey BAD_CHARACTER =
30-
createTextAttributesKey("OPENFGA_BAD_CHARACTER", HighlighterColors.BAD_CHARACTER);
3139

32-
private static final TextAttributesKey[] BAD_CHAR_KEYS = new TextAttributesKey[]{BAD_CHARACTER};
3340
private static final TextAttributesKey[] KEYWORD_KEYS = new TextAttributesKey[]{KEYWORD};
41+
private static final TextAttributesKey[] MODULE_NAME_KEY = new TextAttributesKey[]{MODULE_NAME};
3442
private static final TextAttributesKey[] SCHEMA_VERSIONS_KEYS = new TextAttributesKey[]{SCHEMA_VERSIONS};
3543
private static final TextAttributesKey[] RELATION_NAME_KEYS = new TextAttributesKey[]{RELATION_NAME};
44+
private static final TextAttributesKey[] RELATION_REFERENCE_KEYS = new TextAttributesKey[]{RELATION_REFERENCE};
3645
private static final TextAttributesKey[] TYPE_IDENTIFIER_KEYS = new TextAttributesKey[]{TYPE_IDENTIFIER};
3746
private static final TextAttributesKey[] TYPE_REFERENCE_KEYS = new TextAttributesKey[]{TYPE_REFERENCE};
47+
private static final TextAttributesKey[] CONDITION_IDENTIFIER_KEYS = new TextAttributesKey[]{CONDITION_IDENTIFIER};
48+
private static final TextAttributesKey[] CONDITION_REFERENCE_KEYS = new TextAttributesKey[]{CONDITION_REFERENCE};
49+
private static final TextAttributesKey[] CONDITION_PARAM_KEYS = new TextAttributesKey[]{CONDITION_PARAM_IDENTIFIER};
50+
private static final TextAttributesKey[] CONDITION_PARAM_TYPES = new TextAttributesKey[]{CONDITION_PARAM_TYPE};
3851
private static final TextAttributesKey[] COMMENT_KEYS = new TextAttributesKey[]{COMMENT};
3952
private static final TextAttributesKey[] EMPTY_KEYS = new TextAttributesKey[0];
4053

@@ -50,36 +63,50 @@ public Lexer getHighlightingLexer() {
5063
if (OpenFGATokenSets.SCHEMA_VERSIONS.contains(tokenType)) {
5164
return SCHEMA_VERSIONS_KEYS;
5265
}
53-
if (OpenFGATokenSets.KEYWORDS.contains(tokenType)) {
54-
return KEYWORD_KEYS;
66+
67+
if (tokenType.equals(OpenFGATypes.MODULE_NAME)) {
68+
return MODULE_NAME_KEY;
69+
}
70+
71+
if (tokenType.equals(OpenFGATypes.TYPE_IDENTIFIER)) {
72+
return TYPE_IDENTIFIER_KEYS;
5573
}
74+
if (tokenType.equals(OpenFGATypes.RELATION_DEF_TYPE_RESTRICTION)) {
75+
return TYPE_REFERENCE_KEYS;
76+
}
77+
5678
if (tokenType.equals(OpenFGATypes.RELATION_NAME)) {
5779
return RELATION_NAME_KEYS;
5880
}
59-
if (tokenType.equals(OpenFGATypes.RELATION_DEF_RELATION_ON_SAME_OBJECT)) {
60-
return RELATION_NAME_KEYS;
81+
if (tokenType.equals(OpenFGATypes.REWRITE_TUPLESET_NAME)) {
82+
return RELATION_REFERENCE_KEYS;
83+
}
84+
if (tokenType.equals(OpenFGATypes.REWRITE_COMPUTEDUSERSET_NAME)) {
85+
return RELATION_REFERENCE_KEYS;
6186
}
6287
if (tokenType.equals(OpenFGATypes.REWRITE_TUPLESET_COMPUTEDUSERSET_NAME)) {
63-
return RELATION_NAME_KEYS;
88+
return RELATION_REFERENCE_KEYS;
6489
}
65-
if (tokenType.equals(OpenFGATypes.REWRITE_TUPLESET_NAME)) {
66-
return RELATION_NAME_KEYS;
90+
91+
if (tokenType.equals(OpenFGATypes.CONDITION_NAME_REF)) {
92+
return CONDITION_REFERENCE_KEYS;
6793
}
68-
if (tokenType.equals(OpenFGATypes.RELATION_DEF_TYPE_RESTRICTION_RELATION)) {
69-
return RELATION_NAME_KEYS;
94+
if (tokenType.equals(OpenFGATypes.CONDITION_NAME)) {
95+
return CONDITION_IDENTIFIER_KEYS;
7096
}
71-
if (tokenType.equals(OpenFGATypes.TYPE_IDENTIFIER)) {
72-
return TYPE_IDENTIFIER_KEYS;
97+
if (tokenType.equals(OpenFGATypes.PARAMETER_NAME)) {
98+
return CONDITION_PARAM_KEYS;
7399
}
74-
if (tokenType.equals(OpenFGATypes.RELATION_DEF_TYPE_RESTRICTION_TYPE)) {
75-
return TYPE_REFERENCE_KEYS;
100+
if (tokenType.equals(OpenFGATypes.PARAMETER_TYPE)) {
101+
return CONDITION_PARAM_TYPES;
102+
}
103+
104+
if (OpenFGATokenSets.KEYWORDS.contains(tokenType)) {
105+
return KEYWORD_KEYS;
76106
}
77107
if (OpenFGATokenSets.COMMENTS.contains(tokenType)) {
78108
return COMMENT_KEYS;
79109
}
80-
if (tokenType.equals(TokenType.BAD_CHARACTER)) {
81-
return BAD_CHAR_KEYS;
82-
}
83110

84111
return EMPTY_KEYS;
85112
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
package com.github.le_yams.openfga4intellij.inject;
2+
3+
import com.github.le_yams.openfga4intellij.OpenFGALanguage;
4+
import com.intellij.lang.injection.MultiHostInjector;
5+
import com.intellij.lang.injection.MultiHostRegistrar;
6+
import com.intellij.psi.ElementManipulators;
7+
import com.intellij.psi.PsiElement;
8+
import com.intellij.psi.PsiLanguageInjectionHost;
9+
import org.jetbrains.annotations.NotNull;
10+
import org.jetbrains.yaml.psi.YAMLKeyValue;
11+
import org.jetbrains.yaml.psi.YAMLScalarList;
12+
13+
import java.util.List;
14+
15+
public class OpenFGAYamlInjector implements MultiHostInjector {
16+
@Override
17+
public void getLanguagesToInject(@NotNull MultiHostRegistrar registrar,
18+
@NotNull PsiElement context) {
19+
20+
if (!(context instanceof YAMLKeyValue && shouldInject(context))) {
21+
return;
22+
}
23+
24+
PsiLanguageInjectionHost host = (PsiLanguageInjectionHost) ((YAMLKeyValue) context).getValue();
25+
registrar.startInjecting(OpenFGALanguage.INSTANCE);
26+
registrar.addPlace(null, null, host, ElementManipulators.getValueTextRange(host));
27+
registrar.doneInjecting();
28+
}
29+
30+
private boolean shouldInject(PsiElement context) {
31+
return context instanceof YAMLKeyValue &&
32+
(context.getContainingFile().getName().endsWith("fga.yaml") ||
33+
context.getContainingFile().getName().endsWith("openfga.yaml")) &&
34+
((YAMLKeyValue) context).getValue() instanceof YAMLScalarList &&
35+
((YAMLKeyValue) context).getKeyText().equals("model");
36+
}
37+
38+
@Override
39+
public @NotNull List<? extends Class<? extends PsiElement>> elementsToInjectIn() {
40+
return List.of(YAMLKeyValue.class);
41+
}
42+
}

‎src/main/java/com/github/le_yams/openfga4intellij/parsing/OpenFGALexer.flex

+20-11
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
package com.github.le_yams.openfga4intellij.parsing;
22

33
import com.intellij.lexer.FlexLexer;
4-
import com.intellij.psi.tree.IElementType;
4+
import com.intellij.psi.TokenType;import com.intellij.psi.tree.IElementType;
55

66
import static com.intellij.psi.TokenType.BAD_CHARACTER;
77
import static com.intellij.psi.TokenType.WHITE_SPACE;
@@ -23,40 +23,49 @@ import static com.github.le_yams.openfga4intellij.psi.OpenFGATypes.*;
2323
%unicode
2424

2525
ALPHA_NUMERIC=[a-zA-Z0-9_-]+
26+
SCHEMA_VERSION=(\d+\.\d+)
2627
END_OF_LINE=(\r\n)|\n
2728
WHITESPACE=[\ \t]
2829
IDENT1=((\ {2})|\t)
2930
IDENT2=((\ {4})|(\t{2}))
30-
SINGLE_LINE_COMMENT=[ \t]*#.*
31+
SINGLE_LINE_COMMENT=[ \t]*#+[^\n]*
32+
// SINGLE_TRAILING_COMMENT=\s+#+[^\n]*
33+
CONDITION_EXPRESSION=(\{[^\}]+\})
3134

3235
%%
3336
<YYINITIAL> {
34-
" " { return WHITE_SPACE; }
35-
37+
"module" { return MODULE; }
3638
"model" { return MODEL; }
3739
"schema" { return SCHEMA; }
38-
"1.1" { return SCHEMA_VERSION_V1_1; }
3940
"type" { return TYPE; }
41+
"extend" { return EXTEND; }
4042
"relations" { return RELATIONS; }
4143
"define" { return DEFINE; }
4244
"#" { return HASH; }
4345
":" { return COLON; }
4446
"*" { return WILDCARD; }
4547
"[" { return L_SQUARE; }
4648
"]" { return R_SQUARE; }
49+
"(" { return L_PAREN; }
50+
")" { return R_PAREN; }
51+
"<" { return LESS; }
52+
">" { return GREATER; }
4753
"," { return COMMA; }
4854
"and" { return AND; }
4955
"or" { return OR; }
5056
"but not" { return BUT_NOT; }
5157
"from" { return FROM; }
58+
"condition" { return CONDITION; }
59+
"with" { return WITH; }
5260

5361
{ALPHA_NUMERIC} { return ALPHA_NUMERIC; }
62+
{SCHEMA_VERSION} { return SCHEMA_VERSION; }
5463
{END_OF_LINE} { return END_OF_LINE; }
55-
{WHITESPACE} { return WHITESPACE; }
56-
{IDENT1} { return IDENT1; }
57-
{IDENT2} { return IDENT2; }
58-
{SINGLE_LINE_COMMENT} { return SINGLE_LINE_COMMENT; }
59-
64+
{WHITESPACE} { return TokenType.WHITE_SPACE; }
65+
^{IDENT1} { return IDENT1; }
66+
^{IDENT2} { return IDENT2; }
67+
^{SINGLE_LINE_COMMENT}$ { return SINGLE_LINE_COMMENT; }
68+
{CONDITION_EXPRESSION} { return CONDITION_EXPRESSION; }
6069
}
6170

62-
[^] { return BAD_CHARACTER; }
71+
[^] { return TokenType.BAD_CHARACTER; }

‎src/main/java/com/github/le_yams/openfga4intellij/parsing/OpenFGATokenSets.java

+10-3
Original file line numberDiff line numberDiff line change
@@ -2,27 +2,34 @@
22

33
import com.github.le_yams.openfga4intellij.psi.OpenFGATypes;
44
import com.intellij.psi.tree.TokenSet;
5+
import com.jetbrains.rd.generator.nova.Class;
56

67
public interface OpenFGATokenSets {
78

89
TokenSet KEYWORDS = TokenSet.create(
10+
OpenFGATypes.MODULE,
911
OpenFGATypes.MODEL,
1012
OpenFGATypes.SCHEMA,
1113
OpenFGATypes.TYPE,
14+
OpenFGATypes.EXTEND,
1215
OpenFGATypes.RELATIONS,
1316
OpenFGATypes.DEFINE,
17+
OpenFGATypes.AND,
1418
OpenFGATypes.OR,
15-
OpenFGATypes.FROM
19+
OpenFGATypes.BUT_NOT,
20+
OpenFGATypes.FROM,
21+
OpenFGATypes.CONDITION,
22+
OpenFGATypes.WITH
1623
);
24+
1725
TokenSet COMMENTS = TokenSet.create(
18-
OpenFGATypes.SINGLE_LINE_COMMENT,
1926
OpenFGATypes.HEADER_MULTI_LINE_COMMENT,
2027
OpenFGATypes.MULTI_LINE_COMMENT
2128
);
2229

2330
TokenSet SINGLE_LINE_COMMENT = TokenSet.create(OpenFGATypes.COMMENT);
2431

2532
TokenSet SCHEMA_VERSIONS = TokenSet.create(
26-
OpenFGATypes.SCHEMA_VERSION_V1_1
33+
OpenFGATypes.SCHEMA_VERSION
2734
);
2835
}

‎src/main/java/com/github/le_yams/openfga4intellij/parsing/openfga.bnf

+45-49
Original file line numberDiff line numberDiff line change
@@ -13,69 +13,65 @@
1313
elementTypeClass="com.github.le_yams.openfga4intellij.parsing.OpenFGAElementType"
1414
tokenTypeClass="com.github.le_yams.openfga4intellij.parsing.OpenFGATokenType"
1515

16-
tokens = [
17-
MODEL = "model";
18-
SCHEMA = "schema";
19-
SCHEMA_VERSION_V1_1 = "1.1";
20-
TYPE = "type";
21-
RELATIONS = "relations";
22-
DEFINE = "define";
23-
24-
HASH = "#";
25-
COLON = ":";
26-
WILDCARD = "*";
27-
L_SQUARE = "[";
28-
R_SQUARE = "]";
29-
COMMA = ",";
30-
31-
AND = "and";
32-
OR = "or";
33-
BUT_NOT = "but not";
34-
FROM = "from";
35-
36-
ALPHA_NUMERIC = "regexp:[a-zA-Z0-9_-]+";
37-
38-
END_OF_LINE = "regexp:(\r\n)|\n";
39-
WHITESPACE = "regexp:[\ \t]";
40-
IDENT1= "regexp:((\ {2})|\t)";
41-
IDENT2= "regexp:((\ {4})|(\t{2}))";
42-
43-
SINGLE_LINE_COMMENT = "regexp:[ \t]*#.*"
44-
]
4516
}
4617

4718
openfgaFile ::= main
48-
main ::= modelHeader typeDefs newline?
49-
modelHeader ::= headerMultiLineComment? MODEL spacing? multiLineComment? indentedLine1 SCHEMA spacing schemaVersion spacing?
19+
main ::= headerMultiLineComment? newline? ( modelHeader | moduleHeader ) typeDefs newline? conditionDefs
20+
moduleHeader ::= MODULE moduleName
21+
modelHeader ::= MODEL multiLineComment? indentedLine1 SCHEMA SCHEMA_VERSION
5022
typeDefs ::= typeDef*
51-
typeDef ::= multiLineComment? newline TYPE spacing typeIdentifier spacing? (indentedLine1 RELATIONS spacing? relationDeclaration+)?
52-
relationDeclaration ::= multiLineComment? indentedLine2 DEFINE spacing relationName spacing? COLON spacing? relationDef spacing?
53-
relationDef ::= (relationDefDirectAssignment | relationDefGrouping) relationDefPartials?
54-
relationDefPartials ::= relationDefPartialAllOr | relationDefPartialAllAnd | relationDefPartialAllButNot
55-
relationDefPartialAllOr ::= (spacing OR spacing relationDefGrouping)+
56-
relationDefPartialAllAnd ::= (spacing AND spacing relationDefGrouping)+
57-
relationDefPartialAllButNot ::= (spacing BUT_NOT spacing relationDefGrouping)+
58-
relationDefDirectAssignment ::= L_SQUARE spacing? relationDefTypeRestriction spacing? (COMMA spacing? relationDefTypeRestriction)* spacing? R_SQUARE
23+
typeDef ::= multiLineComment? newline (EXTEND)? TYPE typeIdentifier (indentedLine1 RELATIONS relationDeclaration+)?
24+
relationDeclaration ::= multiLineComment? indentedLine2 DEFINE relationName COLON relationDef
25+
26+
relationDef ::= (relationDefDirectAssignment | relationDefGrouping | relationRecurse) relationDefPartials?
27+
relationDefNoDirect ::= (relationDefGrouping | relationRecurseNoDirect) (relationDefPartials)?;
28+
relationDefPartials ::=
29+
(OR (relationDefGrouping | relationRecurseNoDirect))+
30+
| (AND (relationDefGrouping | relationRecurseNoDirect))+
31+
| (BUT_NOT (relationDefGrouping | relationRecurseNoDirect));
32+
relationRecurse ::= L_PAREN (relationDef | relationRecurseNoDirect) R_PAREN;
33+
relationRecurseNoDirect ::= L_PAREN (relationDefNoDirect | relationRecurseNoDirect) R_PAREN;
34+
relationDefDirectAssignment ::= L_SQUARE relationDefTypeRestriction (COMMA relationDefTypeRestriction)* R_SQUARE
35+
36+
relationDefTypeRestrictionBase ::= relationDefTypeRestrictionWildcard | relationDefTypeRestrictionUserset | relationDefTypeRestrictionRelation
37+
relationDefTypeRestriction ::= relationDefTypeRestrictionBase (WITH conditionNameRef)?
38+
5939
relationDefRewrite ::= relationDefRelationOnRelatedObject | relationDefRelationOnSameObject
6040
relationDefRelationOnSameObject ::= rewriteComputedusersetName
61-
relationDefRelationOnRelatedObject ::= rewriteTuplesetComputedusersetName spacing FROM spacing rewriteTuplesetName
62-
relationDefTypeRestriction ::= relationDefTypeRestrictionWildcard | relationDefTypeRestrictionUserset | relationDefTypeRestrictionType
63-
relationDefTypeRestrictionType ::= identifier
64-
relationDefTypeRestrictionRelation ::= identifier
65-
relationDefTypeRestrictionWildcard ::= relationDefTypeRestrictionType COLON WILDCARD spacing?
66-
relationDefTypeRestrictionUserset ::= relationDefTypeRestrictionType HASH relationDefTypeRestrictionRelation
41+
relationDefRelationOnRelatedObject ::= rewriteTuplesetComputedusersetName FROM rewriteTuplesetName
42+
relationDefTypeRestrictionRelation ::= typeReference
43+
relationDefTypeRestrictionWildcard ::= typeReference COLON WILDCARD
44+
relationDefTypeRestrictionUserset ::= typeReference HASH relationDefTypeRestrictionRelation
6745
relationDefGrouping ::= relationDefRewrite
6846
rewriteComputedusersetName ::= identifier
6947
rewriteTuplesetComputedusersetName ::= identifier
7048
rewriteTuplesetName ::= identifier
49+
moduleName ::= identifier
7150
relationName ::= identifier
7251
typeIdentifier ::= identifier
73-
identifier ::= ALPHA_NUMERIC+
74-
schemaVersion ::= SCHEMA_VERSION_V1_1
75-
spacing ::= WHITESPACE+
52+
typeReference ::= identifier
53+
54+
// Conditions
55+
// https://github.com/google/cel-spec/blob/f9ce1ea675260361a667d29b79ee15fd9768028a/doc/langdef.md#syntax
56+
conditionDefs ::= conditions*
57+
conditions ::= (
58+
(headerMultiLineComment newline)?
59+
CONDITION conditionName
60+
L_PAREN conditionParameter (COMMA conditionParameter)* newline? R_PAREN
61+
CONDITION_EXPRESSION
62+
) newline?
63+
conditionName ::= identifier
64+
conditionNameRef ::= identifier
65+
conditionParameter ::= newline? parameterName COLON parameterType
66+
parameterName ::= identifier
67+
parameterType ::= parameterTypeNames | (("map"|"list") LESS parameterTypeNames GREATER)
68+
parameterTypeNames ::= ("bool"|"string"|"int"|"uint"|"double"|"duration"|"timestamp"|"ipaddress")
69+
70+
// Simple tokens
71+
identifier ::= ALPHA_NUMERIC | MODEL | SCHEMA | TYPE | RELATION | MODULE | EXTEND
7672
indentedLine1 ::= newline IDENT1
7773
indentedLine2 ::= newline IDENT2
7874
newline ::= END_OF_LINE+
7975
comment ::= SINGLE_LINE_COMMENT
8076
headerMultiLineComment ::= comment (newline comment)*
81-
multiLineComment ::= newline comment (newline comment)*
77+
multiLineComment ::= newline comment (newline comment)*

‎src/main/resources/META-INF/plugin.xml

+6
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@
1818
]]></description>
1919

2020
<depends>com.intellij.modules.platform</depends>
21+
<depends>com.intellij.modules.lang</depends>
22+
<depends>org.jetbrains.plugins.yaml</depends>
2123

2224
<extensions defaultExtensionNs="com.intellij">
2325
<fileType
@@ -69,6 +71,10 @@
6971

7072
<notificationGroup id="OpenFGA Notifications"
7173
displayType="BALLOON"/>
74+
<themeProvider id="60bcad08-acdb-40d0-b68a-151e2839f1c4" path="/OpenFGA_Dark.theme.json"/>
75+
76+
<multiHostInjector
77+
implementation="com.github.le_yams.openfga4intellij.inject.OpenFGAYamlInjector"/>
7278
</extensions>
7379
<actions>
7480

+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
{
2+
"name": "OpenFGA Dark",
3+
"dark": true,
4+
"author": "",
5+
"editorScheme": "/OpenFGA_Dark.xml",
6+
"colors": {
7+
"primaryForeground": "#eeffff",
8+
"primaryBackground": "#141517"
9+
},
10+
"ui": {
11+
"*": {
12+
"background": "primaryBackground",
13+
"separatorColor": "primaryForeground",
14+
"foreground": "primaryForeground"
15+
},
16+
"ToolWindow": {
17+
"Header": {
18+
"inactiveBackground": "primaryBackground"
19+
}
20+
}
21+
}
22+
}
23+
24+

‎src/main/resources/OpenFGA_Dark.xml

+75
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
<scheme name="OpenFGA_Dark" version="142" parent_scheme="Darcula">
2+
<colors>
3+
<option name="CARET_ROW_COLOR" value="3A3D41" />
4+
<option name="GUTTER_BACKGROUND" value="141517" />
5+
</colors>
6+
<attributes>
7+
<option name="TEXT">
8+
<value>
9+
<option name="FOREGROUND" value="EEffff" />
10+
<option name="BACKGROUND" value="141517" />
11+
<option name="EFFECT_TYPE" value="5" />
12+
</value>
13+
</option>
14+
<option name="INJECTED_LANGUAGE_FRAGMENT">
15+
<value>
16+
<option name="BACKGROUND" value="141517" />
17+
</value>
18+
</option>
19+
<option name="OPENFGA_KEYWORD">
20+
<value>
21+
<option name="FOREGROUND" value="AAAAAA" />
22+
</value>
23+
</option>
24+
<option name="OPENFGA_MODULE_NAME">
25+
<value>
26+
<option name="FOREGROUND" value="79ED83" />
27+
</value>
28+
</option>
29+
<option name="OPENFGA_COMMENTS">
30+
<value>
31+
<option name="FOREGROUND" value="737981" />
32+
</value>
33+
</option>
34+
<option name="OPENFGA_TYPE_IDENTIFIER">
35+
<value>
36+
<option name="FOREGROUND" value="79ED83" />
37+
</value>
38+
</option>
39+
<option name="OPENFGA_TYPE_REFERENCE">
40+
<value>
41+
<option name="FOREGROUND" value="CEEC93" />
42+
</value>
43+
</option>
44+
<option name="OPENFGA_RELATION_NAME">
45+
<value>
46+
<option name="FOREGROUND" value="20F1F5" />
47+
</value>
48+
</option>
49+
<option name="OPENFGA_RELATION_REFERENCE">
50+
<value>
51+
<option name="FOREGROUND" value="FFFFFF" />
52+
</value>
53+
</option>
54+
<option name="OPENFGA_CONDITION_IDENTIFIER">
55+
<value>
56+
<option name="FOREGROUND" value="79ED83" />
57+
</value>
58+
</option>
59+
<option name="OPENFGA_CONDITION_REFERENCE">
60+
<value>
61+
<option name="FOREGROUND" value="CEEC93" />
62+
</value>
63+
</option>
64+
<option name="OPENFGA_CONDITION_PARAM_IDENTIFIER">
65+
<value>
66+
<option name="FOREGROUND" value="20F1F5" />
67+
</value>
68+
</option>
69+
<option name="OPENFGA_CONDITION_PARAM_TYPE">
70+
<value>
71+
<option name="FOREGROUND" value="AAAAAA" />
72+
</value>
73+
</option>
74+
</attributes>
75+
</scheme>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
package com.github.le_yams.openfga4intellij.parser;
2+
3+
import com.github.le_yams.openfga4intellij.parsing.OpenFGAParserDefinition;
4+
import com.intellij.testFramework.ParsingTestCase;
5+
6+
public class OpenFGAParserTest extends ParsingTestCase {
7+
8+
public OpenFGAParserTest() {
9+
super("", "fga", new OpenFGAParserDefinition());
10+
}
11+
12+
@Override
13+
protected String getTestDataPath() {
14+
return "src/test/testData";
15+
}
16+
17+
public void testschema() throws Throwable {
18+
doCodeTest("""
19+
model
20+
schema 1.1
21+
""");
22+
}
23+
24+
public void testnoschema() throws Throwable {
25+
doCodeTest("""
26+
model
27+
""");
28+
}
29+
30+
public void testuser() throws Throwable {
31+
doCodeTest("""
32+
model
33+
schema 1.1
34+
type user
35+
""");
36+
}
37+
38+
}

‎src/test/testData/noschema.txt

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
OpenFGA File
2+
PsiElement(OpenFGA.MODEL)('model')
3+
PsiElement(OpenFGA.END_OF_LINE)('\n')
4+
PsiErrorElement:OpenFGA.END_OF_LINE, OpenFGA.IDENT1 or OpenFGA.SINGLE_LINE_COMMENT expected
5+
<empty list>

‎src/test/testData/schema.txt

+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
OpenFGA File
2+
OpenFGAMainImpl(MAIN)
3+
OpenFGAModelHeaderImpl(MODEL_HEADER)
4+
PsiElement(OpenFGA.MODEL)('model')
5+
OpenFGAIndentedLine1Impl(INDENTED_LINE_1)
6+
OpenFGANewlineImpl(NEWLINE)
7+
PsiElement(OpenFGA.END_OF_LINE)('\n')
8+
PsiElement(OpenFGA.IDENT1)(' ')
9+
PsiElement(OpenFGA.SCHEMA)('schema')
10+
PsiWhiteSpace(' ')
11+
PsiElement(OpenFGA.SCHEMA_VERSION)('1.1')
12+
OpenFGATypeDefsImpl(TYPE_DEFS)
13+
<empty list>
14+
OpenFGANewlineImpl(NEWLINE)
15+
PsiElement(OpenFGA.END_OF_LINE)('\n')
16+
OpenFGAConditionDefsImpl(CONDITION_DEFS)
17+
<empty list>

‎src/test/testData/user.txt

+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
OpenFGA File
2+
OpenFGAMainImpl(MAIN)
3+
OpenFGAModelHeaderImpl(MODEL_HEADER)
4+
PsiElement(OpenFGA.MODEL)('model')
5+
OpenFGAIndentedLine1Impl(INDENTED_LINE_1)
6+
OpenFGANewlineImpl(NEWLINE)
7+
PsiElement(OpenFGA.END_OF_LINE)('\n')
8+
PsiElement(OpenFGA.IDENT1)(' ')
9+
PsiElement(OpenFGA.SCHEMA)('schema')
10+
PsiWhiteSpace(' ')
11+
PsiElement(OpenFGA.SCHEMA_VERSION)('1.1')
12+
OpenFGATypeDefsImpl(TYPE_DEFS)
13+
OpenFGATypeDefImpl(TYPE_DEF)
14+
OpenFGANewlineImpl(NEWLINE)
15+
PsiElement(OpenFGA.END_OF_LINE)('\n')
16+
PsiElement(OpenFGA.TYPE)('type')
17+
PsiWhiteSpace(' ')
18+
OpenFGATypeIdentifierImpl(TYPE_IDENTIFIER)
19+
OpenFGAIdentifierImpl(IDENTIFIER)
20+
PsiElement(OpenFGA.ALPHA_NUMERIC)('user')
21+
OpenFGANewlineImpl(NEWLINE)
22+
PsiElement(OpenFGA.END_OF_LINE)('\n')
23+
OpenFGAConditionDefsImpl(CONDITION_DEFS)
24+
<empty list>

0 commit comments

Comments
 (0)
Please sign in to comment.