// File, model, tuple and context loading shared by CLI commands.
///|
fn read_text(path : String) -> String raise @fga.FgaError {
@fs.read_file_to_string(path) catch {
_ => raise @fga.fail(@fga.ERR_VALIDATION, "usage: cannot read file " + path)
}
}
///|
fn write_text(path : String, text : String) -> Unit raise @fga.FgaError {
@fs.write_string_to_file(path, text) catch {
_ =>
raise @fga.fail(@fga.ERR_VALIDATION, "usage: cannot write file " + path)
}
}
///|
fn load_model_file(path : String) -> @fga.AuthModel raise @fga.FgaError {
@model.model_from_source(read_text(path))
}
///|
fn tuples_from_value(value : Json) -> Array[@fga.Tuple] raise @fga.FgaError {
match value {
Array(_) => @fga.tuples_from_json(Some(value))
Object(m) =>
match m.get("tuples") {
Some(tuples) => @fga.tuples_from_json(Some(tuples))
None =>
raise @fga.fail(
@fga.ERR_VALIDATION,
"tuple document must be an array or an object with 'tuples'",
)
}
_ =>
raise @fga.fail(
@fga.ERR_VALIDATION,
"tuple document must be an array or object",
)
}
}
///|
fn load_tuples_file(path : String) -> Array[@fga.Tuple] raise @fga.FgaError {
let source = read_text(path)
let trimmed = source.trim()
if trimmed == "" {
return []
}
if trimmed.has_prefix("[") || trimmed.has_prefix("{") {
let parsed = @json.parse(source) catch {
_ =>
raise @fga.fail(
@fga.ERR_VALIDATION,
"usage: invalid JSON tuples in " + path,
)
}
tuples_from_value(parsed)
} else if path.has_suffix(".yaml") || path.has_suffix(".yml") {
let documents = @yaml.Yaml::load_from_string(source) catch {
_ =>
raise @fga.fail(
@fga.ERR_VALIDATION,
"usage: invalid YAML tuples in " + path,
)
}
if documents.length() == 0 {
[]
} else {
tuples_from_value(documents[0].to_json())
}
} else {
@fga.tuples_from_text(source)
}
}
///|
fn load_optional_tuples(
args : CliArgs,
flag : String,
) -> Array[@fga.Tuple] raise @fga.FgaError {
match flag_value(args, flag) {
Some(path) => load_tuples_file(path)
None => []
}
}
///|
fn parse_context_text(text : String) -> Map[String, Json] raise @fga.FgaError {
let parsed = @json.parse(text) catch {
_ =>
raise @fga.fail(
@fga.ERR_VALIDATION,
"usage: --context must be a JSON object",
)
}
match parsed {
Object(m) => m
_ =>
raise @fga.fail(
@fga.ERR_VALIDATION,
"usage: --context must be a JSON object",
)
}
}
///|
fn load_context(args : CliArgs) -> Map[String, Json] raise @fga.FgaError {
match flag_value(args, "--context") {
Some(text) => parse_context_text(text)
None =>
match flag_value(args, "--context-file") {
Some(path) => parse_context_text(read_text(path))
None => Map([])
}
}
}
///|
fn parse_filters(args : CliArgs, model : @fga.AuthModel) -> Array[String] {
match flag_value(args, "--filters") {
Some(text) => {
let out : Array[String] = []
for part in text.split(",") {
let filter = part.trim().to_owned()
if filter != "" {
out.push(filter)
}
}
out
}
None => {
let out : Array[String] = []
for type_def in model.types {
out.push(type_def.name)
}
out
}
}
}
///|
fn dirname(path : String) -> String {
let mut last = -1
for i in 0.. last = i
_ => ()
}
}
if last < 0 {
""
} else {
path[:last + 1].to_owned()
}
}