Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
93 commits
Select commit Hold shift + click to select a range
f6f0d31
Add implementation plan for __EMBED__ convention in struct walkers
denik Mar 10, 2026
c8a9e5c
Add EmbedTagName constant and IsEmbed() method to structtag
denik Mar 10, 2026
a423c1e
Support __EMBED__ tag in structwalk.Walk
denik Mar 10, 2026
089debc
Support __EMBED__ tag in structwalk.WalkType
denik Mar 10, 2026
e3063b4
Support __EMBED__ tag in structaccess.Get
denik Mar 10, 2026
45b25d4
Support __EMBED__ tag in structaccess.Set
denik Mar 10, 2026
0fa70ab
Support __EMBED__ tag in structaccess.ValidatePath
denik Mar 10, 2026
a079c18
Support __EMBED__ tag in structdiff.GetStructDiff
denik Mar 10, 2026
c734b34
Use __EMBED__ tag on PermissionsState.Permissions
denik Mar 10, 2026
6b510f5
Enable direct engine for job_permissions acceptance test
denik Mar 10, 2026
cd98785
Regenerate out.test.toml for job_permissions test
denik Mar 10, 2026
cf33a6c
Add custom JSON marshaling for PermissionsState and regenerate outputs
denik Mar 10, 2026
40bfd97
add REQUEST / RESPOSNE
denik Mar 10, 2026
c45c8d4
move into TASKS
denik Mar 10, 2026
4c65311
Add task 002 and claude log
denik Mar 10, 2026
eb831ad
Add plan for task 002: refine __EMBED__ convention
denik Mar 10, 2026
858f6dd
Switch embed detection from json tag to field name
denik Mar 10, 2026
aaab916
Rename Permissions to EmbeddedSlice, remove custom JSON marshaling
denik Mar 10, 2026
c8846db
Fix formatting after pre-commit hook
denik Mar 10, 2026
fa1dc33
Fix trailing whitespace in TASKS/001.md
denik Mar 10, 2026
35142e2
Add status for task 002
denik Mar 10, 2026
6eddc56
Add PR title and description
denik Mar 10, 2026
82fda0e
Update PR title and description
denik Mar 11, 2026
9c71363
Add changelog entry for permissions state path fix
denik Mar 11, 2026
2138f66
Add PR link to changelog entry
denik Mar 11, 2026
b2fe80b
clean up TASKS
denik Mar 11, 2026
050d9c1
rename field to _
denik Mar 11, 2026
6fa0192
update after rebase
denik Mar 11, 2026
99a215c
Remove duplicate changelog entry
denik Mar 11, 2026
7fed34f
Move EmbeddedSliceFieldName from structtag to structaccess
denik Mar 11, 2026
3bafc16
Revert "clean up TASKS"
denik Mar 11, 2026
a8de2cf
Add task 004: acceptance tests for permission references
denik Mar 11, 2026
c07f196
Add plan for task 004: acceptance tests for permission references
denik Mar 11, 2026
87f4424
Fix reference resolution for permission sub-resources
denik Mar 11, 2026
1f9ef86
Add acceptance test for permission reference resolution
denik Mar 11, 2026
88d8282
Fix trailing whitespace in TASKS/004.md
denik Mar 11, 2026
f5c0ef8
Simplify permission_ref test: plan + deploy without request recording
denik Mar 11, 2026
ee35420
Update status and PR description for task 004
denik Mar 11, 2026
3130008
Add task 005: use bundle config Permission type in PermissionsState
denik Mar 11, 2026
7a530c9
Add plan for task 005: use Permission type in PermissionsState
denik Mar 11, 2026
8b5b6bd
Use resources.Permission in PermissionsState instead of iam.AccessCon…
denik Mar 11, 2026
eab7338
Update refschema and fix exhaustruct lint for Permission type change
denik Mar 11, 2026
77665e0
Update acceptance test outputs: permission_level -> level in direct e…
denik Mar 11, 2026
53cf2b3
Add task 005 status and task 006
denik Mar 11, 2026
07d255c
Add plan for task 006: permission_level → level state migration
denik Mar 11, 2026
ffd621a
Add permission_level → level migration for backward-compatible state …
denik Mar 11, 2026
55772fc
Update refschema output for StatePermission type
denik Mar 11, 2026
9c95415
Add acceptance test for permission_level → level state migration
denik Mar 11, 2026
c7e41d6
Fix exhaustruct lint for StatePermission
denik Mar 11, 2026
38316f8
Update task 006 status and PR description
denik Mar 11, 2026
8bc1468
Add task 007: state migration for permissions
denik Mar 12, 2026
83a2044
Add plan for task 007: state migration system
denik Mar 12, 2026
daca030
Add state migration system and move permissions migration to dstate/m…
denik Mar 12, 2026
5060b7d
Update migration test to use old v1 state format with permissions key
denik Mar 12, 2026
6b1d59f
Update acceptance test output for state version 2 migration
denik Mar 12, 2026
45ef725
Update generated output: remove permission_level from StatePermission
denik Mar 12, 2026
1ef280e
Handle state_version 0 (absent field) as version 1 in migration
denik Mar 12, 2026
28dd02f
Update acceptance test outputs for state_version 2
denik Mar 12, 2026
f78698b
Update task 007 status and PR description
denik Mar 12, 2026
5b991fa
clean up
denik Mar 12, 2026
5308fa2
Move embedded state fixture to separate file in permission_level_migr…
denik Mar 13, 2026
6a36204
update cloud tests
denik Mar 13, 2026
a7e650a
clean up TASKS
denik Mar 13, 2026
ebb6624
Reuse dresources.PermissionsState in dstate migration instead of loca…
denik Mar 13, 2026
716d1a3
Add job_permission_ref invariant test; separate stderr in plan commands
denik Mar 13, 2026
bde5897
fix out
denik Mar 13, 2026
74b0f02
Exclude job_permission_ref from migrate invariant test
denik Mar 13, 2026
669297b
Add job_cross_resource_ref invariant test; improve migrate exclusion …
denik Mar 13, 2026
1807c08
update out.test.toml
denik Mar 13, 2026
38e0d84
Add continuity invariant test for state migration from v0.293.0
denik Mar 13, 2026
547318f
Replace continuity test with continue_293 that deploys with v0.293.0 …
denik Mar 13, 2026
8de06ad
Exclude permission ref configs from cloud runs in no_drift test
denik Mar 13, 2026
00267b5
Use built-in groups (users, admins) in permission ref invariant configs
denik Mar 13, 2026
60ae21e
record version, remove unnecessary trace
denik Mar 15, 2026
1eeee9b
Fix continue_293 exclusion comment: permissions cross-refs require fe…
denik Mar 16, 2026
c1ed8cc
Add job_with_permissions invariant config; test state migration conti…
denik Mar 16, 2026
23afaa6
Clean up unnecessary changes: restore comments, simplify intermediate…
denik Mar 16, 2026
a122632
Error on state version newer than supported instead of silently skipping
denik Mar 16, 2026
e71d844
Remove migrate_test.go; migration is covered by acceptance/bundle/sta…
denik Mar 16, 2026
f1d93a8
Add acceptance test for future state version error
denik Mar 16, 2026
ecfd4cf
Exclude permissions and grants from parent resource walk in refschema
denik Mar 16, 2026
314b70d
normalize NEXT_CHANGELOG
denik Mar 16, 2026
56f4311
update NEXT_CHANGELOG
denik Mar 16, 2026
e80aca2
update tests
denik Mar 16, 2026
41f54e6
Cache EmbeddedSlice field index and unify embed field lookup
denik Mar 17, 2026
50090e4
Make continue_293 test as slow
denik Mar 17, 2026
4f681e9
update test output
denik Mar 17, 2026
3c15b24
rename to __embed__
denik Mar 17, 2026
67c3474
update cloud tests
denik Mar 17, 2026
a8bfb73
disable flaky check
denik Mar 17, 2026
8b402e0
add a comment
denik Mar 17, 2026
051b54a
record result on TF but not on direct where it's flaky
denik Mar 17, 2026
79d09ab
restore NEXT_CHANGELOG
denik Mar 17, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions NEXT_CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

### Bundles
* engine/direct: Fix permanent drift on experiment name field ([#4627](https://github.com/databricks/cli/pull/4627))
* engine/direct: Fix permissions state path to match input config schema ([#4703](https://github.com/databricks/cli/pull/4703))

### Dependency updates

Expand Down
6 changes: 6 additions & 0 deletions acceptance/acceptance_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,12 @@ func testAccept(t *testing.T, inprocessMode bool, singleTest string) int {
t.Setenv("CLI", execPath)
repls.SetPath(execPath, "[CLI]")

if !inprocessMode {
cli293Path := DownloadCLI(t, buildDir, "0.293.0")
t.Setenv("CLI_293", cli293Path)
repls.SetPath(cli293Path, "[CLI_293]")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like the idea but also comes at a cost.

E.g. I run in fresh worktrees all the time this would download all the time. I wonder if we can make this optional per test, so if you're working on something that doesn't need to perform a backward compat check or upgrade check you don't have to download this binary.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could store that cache in ~/.databricks-cli-tests-cache to help with this (as a follow up).

}

paths := []string{
// Make helper scripts available
filepath.Join(cwd, "bin"),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
true
2 changes: 1 addition & 1 deletion acceptance/bundle/apps/job_permissions/out.test.toml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 0 additions & 1 deletion acceptance/bundle/apps/job_permissions/output.txt
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ Deployment complete!

=== After first deploy
>>> has_manage_run
true

=== After second deploy
>>> [CLI] bundle deploy
Expand Down
13 changes: 12 additions & 1 deletion acceptance/bundle/apps/job_permissions/script
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,18 @@ has_manage_run() {
}

title "After first deploy"
trace has_manage_run
trace has_manage_run > out.after_first_deploy.$DATABRICKS_BUNDLE_ENGINE.txt
# This is flaky on direct, because there are (at least) two ways to complete deployment:
# sequence 1:
# job is deployed
# job permissions are deployed
# app is deployed
# sequence 2:
# job is deployed
# app is deployed, updates job permissions
# job permissions are deployed
# (It does not appear flaky on TF, maybe there is enough delay in one of the resources)
rm -f out.after_first_deploy.direct.txt

title "After second deploy"
trace $CLI bundle deploy
Expand Down
8 changes: 1 addition & 7 deletions acceptance/bundle/apps/job_permissions/test.toml
Original file line number Diff line number Diff line change
@@ -1,11 +1,5 @@
# Direct engine error: cannot plan resources.jobs.my_job.permissions: cannot update
# [0].service_principal_name: failed to navigate to parent [0]: [0]: cannot index struct.
# This is a bug in structaccess.Set() where it fails to index into a struct when
# setting permissions with service_principal_name.
# See https://github.com/databricks/cli/pull/4644
Badness = "Direct engine fails to plan permissions with service_principal_name on jobs"
Cloud = true
RecordRequests = false

[EnvMatrix]
DATABRICKS_BUNDLE_ENGINE = ["terraform"]
DATABRICKS_BUNDLE_ENGINE = ["terraform", "direct"]
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"state_version": 1,
"state_version": 2,
"cli_version": "[DEV_VERSION]",
"lineage": "[UUID]",
"serial": 2,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
bundle:
name: test-bundle-$UNIQUE_NAME

resources:
jobs:
# job_src defines permissions and a tag value used as references by other resources
job_src:
name: test-job-src-$UNIQUE_NAME
tags:
perm_group: users
permissions:
- level: CAN_VIEW
group_name: users

# job_perm_ref uses permission fields from job_src as its permission values
job_perm_ref:
name: test-job-perm-ref-$UNIQUE_NAME
permissions:
- level: ${resources.jobs.job_src.permissions[0].level}
group_name: ${resources.jobs.job_src.permissions[0].group_name}

# job_tag_ref uses a job tag from job_src as a permission group_name
job_tag_ref:
name: test-job-tag-ref-$UNIQUE_NAME
permissions:
- level: CAN_VIEW
group_name: ${resources.jobs.job_src.tags.perm_group}
21 changes: 21 additions & 0 deletions acceptance/bundle/invariant/configs/job_permission_ref.yml.tmpl
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
bundle:
name: test-bundle-$UNIQUE_NAME

resources:
jobs:
job_b:
name: test-job-b-$UNIQUE_NAME
permissions:
- level: CAN_VIEW
group_name: users
- level: CAN_MANAGE
group_name: admins

job_a:
name: test-job-a-$UNIQUE_NAME
permissions:
# Reference level and group_name from job_b by index
- level: ${resources.jobs.job_b.permissions[0].level}
group_name: ${resources.jobs.job_b.permissions[0].group_name}
- level: ${resources.jobs.job_b.permissions[1].level}
group_name: ${resources.jobs.job_b.permissions[1].group_name}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
bundle:
name: test-bundle-$UNIQUE_NAME

resources:
jobs:
foo:
name: test-job-$UNIQUE_NAME
permissions:
- level: CAN_VIEW
group_name: users
7 changes: 7 additions & 0 deletions acceptance/bundle/invariant/continue_293/out.test.toml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions acceptance/bundle/invariant/continue_293/output.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@

>>> [CLI_293] --version
Databricks CLI v0.293.0
INPUT_CONFIG_OK
41 changes: 41 additions & 0 deletions acceptance/bundle/invariant/continue_293/script
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# Invariant to test: current CLI can deploy on top of state produced by v0.293.0

cp -r "$TESTDIR/../data/." . &> LOG.cp

INIT_SCRIPT="$TESTDIR/../configs/$INPUT_CONFIG-init.sh"
if [ -f "$INIT_SCRIPT" ]; then
source "$INIT_SCRIPT" &> LOG.init
fi

envsubst < "$TESTDIR/../configs/$INPUT_CONFIG" > databricks.yml

cleanup() {
$CLI bundle destroy --auto-approve &> LOG.destroy
cat LOG.destroy | contains.py '!panic' '!internal error' > /dev/null

CLEANUP_SCRIPT="$TESTDIR/../configs/$INPUT_CONFIG-cleanup.sh"
if [ -f "$CLEANUP_SCRIPT" ]; then
source "$CLEANUP_SCRIPT" &> LOG.cleanup
fi
}

trap cleanup EXIT

# Deploy with old CLI to produce v0.293.0 state
trace $CLI_293 --version
$CLI_293 bundle deploy &> LOG.deploy.293
cat LOG.deploy.293 | contains.py '!panic' '!internal error' > /dev/null

echo INPUT_CONFIG_OK

# Deploy with current CLI on top of old state
$CLI bundle deploy &> LOG.deploy
cat LOG.deploy | contains.py '!panic' '!internal error' > /dev/null

# Verify no drift after current CLI deploy
$CLI bundle plan -o json > LOG.planjson 2>LOG.planjson.err
cat LOG.planjson.err | contains.py '!panic' '!internal error' > /dev/null
verify_no_drift.py LOG.planjson

$CLI bundle plan 2>LOG.plan.err | contains.py '!panic' '!internal error' 'Plan: 0 to add, 0 to change, 0 to delete' > LOG.plan
cat LOG.plan.err | contains.py '!panic' '!internal error' > /dev/null
7 changes: 7 additions & 0 deletions acceptance/bundle/invariant/continue_293/test.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
Cloud = false
Slow = true

# Cross-resource permission references (${resources.jobs.X.permissions[N].field}) require
# permissions to be part of the job schema, which was added after v0.293.0.
EnvMatrixExclude.no_permission_ref = ["INPUT_CONFIG=job_permission_ref.yml.tmpl"]
EnvMatrixExclude.no_cross_resource_ref = ["INPUT_CONFIG=job_cross_resource_ref.yml.tmpl"]
2 changes: 1 addition & 1 deletion acceptance/bundle/invariant/migrate/out.test.toml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions acceptance/bundle/invariant/migrate/script
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,6 @@ trace $CLI bundle deployment migrate &> LOG.migrate

cat LOG.migrate | contains.py '!panic:' '!internal error' > /dev/null

$CLI bundle plan -o json &> plan.json
cat plan.json | contains.py '!panic:' '!internal error' > /dev/null
$CLI bundle plan -o json > plan.json 2>plan.json.err
cat plan.json.err | contains.py '!panic:' '!internal error' > /dev/null
verify_no_drift.py plan.json
9 changes: 9 additions & 0 deletions acceptance/bundle/invariant/migrate/test.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,12 @@ EnvMatrixExclude.no_external_location = ["INPUT_CONFIG=external_location.yml.tmp

# Unexpected action='create' for resources.secret_scopes.foo.permissions
EnvMatrixExclude.no_secret_scope = ["INPUT_CONFIG=secret_scope.yml.tmpl"]

# Cross-resource permission references (e.g. ${resources.jobs.job_b.permissions[0].level})
# don't work in terraform mode: the terraform interpolator converts the path to
# ${databricks_job.job_b.permissions[0].level}, but Terraform's databricks_job resource
# does not expose permissions as output attributes (permissions are a separate
# databricks_permissions resource in terraform), so the literal unresolved string
# ends up as the permission level value.
EnvMatrixExclude.no_permission_ref = ["INPUT_CONFIG=job_permission_ref.yml.tmpl"]
EnvMatrixExclude.no_cross_resource_ref = ["INPUT_CONFIG=job_cross_resource_ref.yml.tmpl"]
2 changes: 1 addition & 1 deletion acceptance/bundle/invariant/no_drift/out.test.toml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 4 additions & 3 deletions acceptance/bundle/invariant/no_drift/script
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,9 @@ echo INPUT_CONFIG_OK

# Check both text and JSON plan for no changes
# Note, expect that there maybe more than one resource unchanged
$CLI bundle plan -o json &> LOG.planjson
cat LOG.planjson | contains.py '!panic' '!internal error' > /dev/null
$CLI bundle plan -o json > LOG.planjson 2>LOG.planjson.err
cat LOG.planjson.err | contains.py '!panic' '!internal error' > /dev/null
verify_no_drift.py LOG.planjson

$CLI bundle plan | contains.py '!panic' '!internal error' 'Plan: 0 to add, 0 to change, 0 to delete' > LOG.plan
$CLI bundle plan 2>LOG.plan.err | contains.py '!panic' '!internal error' 'Plan: 0 to add, 0 to change, 0 to delete' > LOG.plan
cat LOG.plan.err | contains.py '!panic' '!internal error' > /dev/null
4 changes: 4 additions & 0 deletions acceptance/bundle/invariant/test.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ Ignore = [
"plan.json",
"*.py",
"*.json",
"*.err",
"app",
]

Expand All @@ -32,6 +33,9 @@ EnvMatrix.INPUT_CONFIG = [
"job.yml.tmpl",
"job_pydabs_10_tasks.yml.tmpl",
"job_pydabs_1000_tasks.yml.tmpl",
"job_cross_resource_ref.yml.tmpl",
"job_permission_ref.yml.tmpl",
"job_with_permissions.yml.tmpl",
"job_with_task.yml.tmpl",
"model.yml.tmpl",
"model_serving_endpoint.yml.tmpl",
Expand Down
2 changes: 1 addition & 1 deletion acceptance/bundle/migrate/basic/out.new_state.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"state_version": 1,
"state_version": 2,
"cli_version": "[DEV_VERSION]",
"lineage": "[UUID]",
"serial": 6,
Expand Down
2 changes: 1 addition & 1 deletion acceptance/bundle/migrate/dashboards/out.new_state.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"state_version": 1,
"state_version": 2,
"cli_version": "[DEV_VERSION]",
"lineage": "[UUID]",
"serial": 3,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"state_version": 1,
"state_version": 2,
"cli_version": "[DEV_VERSION]",
"lineage": "[UUID]",
"serial": 5,
Expand Down
2 changes: 1 addition & 1 deletion acceptance/bundle/migrate/grants/out.new_state.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"state_version": 1,
"state_version": 2,
"cli_version": "[DEV_VERSION]",
"lineage": "[UUID]",
"serial": 9,
Expand Down
14 changes: 7 additions & 7 deletions acceptance/bundle/migrate/permissions/out.new_state.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"state_version": 1,
"state_version": 2,
"cli_version": "[DEV_VERSION]",
"lineage": "[UUID]",
"serial": 7,
Expand Down Expand Up @@ -32,13 +32,13 @@
"__id__": "/jobs/[NUMID]",
"state": {
"object_id": "/jobs/[NUMID]",
"permissions": [
"__embed__": [
{
"permission_level": "CAN_VIEW",
"level": "CAN_VIEW",
"user_name": "viewer@databricks.com"
},
{
"permission_level": "IS_OWNER",
"level": "IS_OWNER",
"user_name": "[USERNAME]"
}
]
Expand Down Expand Up @@ -73,13 +73,13 @@
"__id__": "/pipelines/[UUID]",
"state": {
"object_id": "/pipelines/[UUID]",
"permissions": [
"__embed__": [
{
"permission_level": "CAN_MANAGE",
"level": "CAN_MANAGE",
"user_name": "manager@databricks.com"
},
{
"permission_level": "IS_OWNER",
"level": "IS_OWNER",
"user_name": "[USERNAME]"
}
]
Expand Down
10 changes: 5 additions & 5 deletions acceptance/bundle/migrate/runas/out.new_state.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"state_version": 1,
"state_version": 2,
"cli_version": "[DEV_VERSION]",
"lineage": "[UUID]",
"serial": 5,
Expand Down Expand Up @@ -33,13 +33,13 @@
"__id__": "/pipelines/[UUID]",
"state": {
"object_id": "/pipelines/[UUID]",
"permissions": [
"__embed__": [
{
"group_name": "users",
"permission_level": "CAN_VIEW"
"level": "CAN_VIEW",
"group_name": "users"
},
{
"permission_level": "IS_OWNER",
"level": "IS_OWNER",
"user_name": "[USERNAME]"
}
]
Expand Down
8 changes: 4 additions & 4 deletions acceptance/bundle/migrate/runas/out.plan.json
Original file line number Diff line number Diff line change
Expand Up @@ -55,13 +55,13 @@
"action": "skip",
"remote_state": {
"object_id": "/pipelines/[UUID]",
"permissions": [
"__embed__": [
{
"group_name": "users",
"permission_level": "CAN_VIEW"
"level": "CAN_VIEW",
"group_name": "users"
},
{
"permission_level": "IS_OWNER",
"level": "IS_OWNER",
"user_name": "[USERNAME]"
}
]
Expand Down
Loading
Loading