From cc0bf3467b33c32922ec78eb0cf4fffbd90483c2 Mon Sep 17 00:00:00 2001 From: Anders Betts Date: Sun, 20 Sep 2026 09:35:06 +0200 Subject: commands: declarative argument schemas with dispatch validation --- src/commands.c | 669 +++++++++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 601 insertions(+), 68 deletions(-) (limited to 'src/commands.c') diff --git a/src/commands.c b/src/commands.c index d21d4c8..d639183 100644 --- a/src/commands.c +++ b/src/commands.c @@ -63,6 +63,134 @@ static int arg_bool(yyjson_val *args, const char *key, int *out) return 1; } +static const char *arg_type_name(enum arg_type type) +{ + switch (type) { + case ARG_STR: + return "string"; + case ARG_INT: + return "int"; + case ARG_BOOL: + return "bool"; + case ARG_ENUM: + return "enum"; + case ARG_DATE: + return "date"; + case ARG_JSON: + return "json"; + } + return "json"; +} + +static int enum_allowed(const char *values, const char *v) +{ + size_t n = strlen(v); + const char *p = values; + while (p && *p) { + const char *comma = strchr(p, ','); + size_t len = comma ? (size_t)(comma - p) : strlen(p); + if (len == n && strncmp(p, v, n) == 0) + return 1; + if (!comma) + break; + p = comma + 1; + } + return 0; +} + +static void arg_error(char *err, size_t errlen, const char *fmt, ...) + __attribute__((format(printf, 3, 4))); + +static void arg_error(char *err, size_t errlen, const char *fmt, ...) +{ + if (!err || errlen == 0) + return; + va_list ap; + va_start(ap, fmt); + vsnprintf(err, errlen, fmt, ap); + va_end(ap); +} + +int command_validate_args(const struct command *cmd, yyjson_val *args, + char *err, size_t errlen) +{ + if (!cmd || !cmd->args) + return 0; + if (err && errlen) + err[0] = '\0'; + for (size_t i = 0; i < cmd->nargs; i++) { + const struct cmd_arg *a = &cmd->args[i]; + yyjson_val *v = args && yyjson_is_obj(args) + ? yyjson_obj_get(args, a->name) + : NULL; + if (!v || yyjson_is_null(v)) { + if (a->required) { + arg_error(err, errlen, "missing required argument \"%s\"", + a->name); + return -1; + } + continue; + } + int bad = 0, empty = 0; + switch (a->type) { + case ARG_STR: + bad = !yyjson_is_str(v); + empty = !bad && a->required && !*yyjson_get_str(v); + break; + case ARG_INT: + bad = !yyjson_is_int(v); + break; + case ARG_BOOL: + bad = !yyjson_is_bool(v); + break; + case ARG_ENUM: + bad = !yyjson_is_str(v) || + !enum_allowed(a->values, yyjson_get_str(v)); + empty = !bad && a->required && !*yyjson_get_str(v); + break; + case ARG_DATE: + bad = !yyjson_is_str(v) || + !util_parse_iso_date(yyjson_get_str(v)); + break; + case ARG_JSON: + break; + } + if (empty) { + arg_error(err, errlen, "missing required argument \"%s\"", + a->name); + return -1; + } + if (bad) { + switch (a->type) { + case ARG_STR: + arg_error(err, errlen, "argument \"%s\" must be a string", + a->name); + break; + case ARG_INT: + arg_error(err, errlen, "argument \"%s\" must be an integer", + a->name); + break; + case ARG_BOOL: + arg_error(err, errlen, "argument \"%s\" must be a boolean", + a->name); + break; + case ARG_ENUM: + arg_error(err, errlen, "argument \"%s\" must be one of: %s", + a->name, a->values ? a->values : ""); + break; + case ARG_DATE: + arg_error(err, errlen, "argument \"%s\" must be YYYY-MM-DD", + a->name); + break; + case ARG_JSON: + break; + } + return -1; + } + } + return 0; +} + static yyjson_mut_val *fail(struct req *r, const char *code, const char *msg) { r->err_code = code; @@ -1405,6 +1533,33 @@ static yyjson_mut_val *command_json(struct req *r, const struct command *c) yyjson_mut_obj_add_null(r->rdoc, perm, "scope"); yyjson_mut_obj_add_bool(r->rdoc, perm, "require_org", c->need_org != 0); yyjson_mut_obj_add_val(r->rdoc, o, "permission", perm); + yyjson_mut_val *args = yyjson_mut_arr(r->rdoc); + for (size_t i = 0; i < c->nargs; i++) { + const struct cmd_arg *a = &c->args[i]; + yyjson_mut_val *ao = yyjson_mut_arr_add_obj(r->rdoc, args); + yyjson_mut_obj_add_strcpy(r->rdoc, ao, "name", a->name); + yyjson_mut_obj_add_strcpy(r->rdoc, ao, "type", + arg_type_name(a->type)); + yyjson_mut_obj_add_bool(r->rdoc, ao, "required", a->required != 0); + if (a->def) + yyjson_mut_obj_add_strcpy(r->rdoc, ao, "default", a->def); + if (a->values) { + yyjson_mut_val *vals = yyjson_mut_arr(r->rdoc); + const char *p = a->values; + while (*p) { + const char *comma = strchr(p, ','); + size_t len = comma ? (size_t)(comma - p) : strlen(p); + yyjson_mut_arr_add_strn(r->rdoc, vals, p, len); + if (!comma) + break; + p = comma + 1; + } + yyjson_mut_obj_add_val(r->rdoc, ao, "values", vals); + } + if (a->desc) + yyjson_mut_obj_add_strcpy(r->rdoc, ao, "description", a->desc); + } + yyjson_mut_obj_add_val(r->rdoc, o, "args", args); return o; } @@ -4482,135 +4637,513 @@ static yyjson_mut_val *h_sie_import(struct req *r) /* command table */ /* ------------------------------------------------------------------ */ +static const struct cmd_arg args_session_open[] = { + { "method", ARG_ENUM, 1, NULL, "password,token", + "Open with a password or an API token" }, + { "username", ARG_STR, 0, NULL, NULL, "Username for method=password" }, + { "password", ARG_STR, 0, NULL, NULL, "Password for method=password" }, + { "token", ARG_STR, 0, NULL, NULL, "API token for method=token" }, +}; + +static const struct cmd_arg args_session_use_org[] = { + { "org", ARG_INT, 1, NULL, NULL, "Org id" }, +}; + +static const struct cmd_arg args_org_create[] = { + { "name", ARG_STR, 1, NULL, NULL, "Org name" }, + { "org_nr", ARG_STR, 0, NULL, NULL, "Swedish org number" }, + { "fiscal_year_start_month", ARG_INT, 0, "1", NULL, + "Fiscal year start month 1-12" }, + { "moms_period", ARG_ENUM, 0, "month", "month,quarter,year", + "VAT reporting period" }, + { "framework", ARG_ENUM, 0, "K2", "K2,K3", "Accounting framework" }, +}; + +static const struct cmd_arg args_org_update[] = { + { "name", ARG_STR, 0, NULL, NULL, "Org name" }, + { "org_nr", ARG_STR, 0, NULL, NULL, "Swedish org number" }, + { "vat_nr", ARG_STR, 0, NULL, NULL, "VAT number" }, + { "address", ARG_STR, 0, NULL, NULL, "Street address" }, + { "postal_code", ARG_STR, 0, NULL, NULL, "Postal code" }, + { "city", ARG_STR, 0, NULL, NULL, "City" }, + { "country", ARG_STR, 0, NULL, NULL, "Country" }, + { "email", ARG_STR, 0, NULL, NULL, "E-mail" }, + { "phone", ARG_STR, 0, NULL, NULL, "Phone" }, + { "moms_period", ARG_ENUM, 0, NULL, "month,quarter,year", + "VAT reporting period" }, + { "framework", ARG_ENUM, 0, NULL, "K2,K3", "Accounting framework" }, + { "description", ARG_STR, 0, NULL, NULL, "Free-text description" }, + { "fiscal_year_start_month", ARG_INT, 0, NULL, NULL, + "Fiscal year start month 1-12" }, + { "shares", ARG_INT, 0, NULL, NULL, "Number of shares" }, +}; + +static const struct cmd_arg args_member_role[] = { + { "username", ARG_STR, 1, NULL, NULL, "Username" }, + { "role", ARG_ENUM, 1, NULL, "owner,bookkeeper,viewer", + "Membership role" }, +}; + +static const struct cmd_arg args_member_username[] = { + { "username", ARG_STR, 1, NULL, NULL, "Username" }, +}; + +static const struct cmd_arg args_board_add[] = { + { "name", ARG_STR, 1, NULL, NULL, "Board member name" }, + { "title", ARG_STR, 0, "Styrelseledamot", NULL, "Role on the board" }, +}; + +static const struct cmd_arg args_board_update[] = { + { "id", ARG_INT, 1, NULL, NULL, "Board member id" }, + { "name", ARG_STR, 0, NULL, NULL, "Board member name" }, + { "title", ARG_STR, 0, NULL, NULL, "Role on the board" }, +}; + +static const struct cmd_arg args_board_remove[] = { + { "id", ARG_INT, 1, NULL, NULL, "Board member id" }, +}; + +static const struct cmd_arg args_user_create[] = { + { "username", ARG_STR, 1, NULL, NULL, "Username" }, + { "password", ARG_STR, 1, NULL, NULL, "Password" }, + { "display_name", ARG_STR, 0, NULL, NULL, "Display name" }, + { "is_admin", ARG_BOOL, 0, "false", NULL, "System admin" }, +}; + +static const struct cmd_arg args_token_create[] = { + { "label", ARG_STR, 1, NULL, NULL, "Label shown in token.list" }, + { "scopes", ARG_JSON, 0, NULL, NULL, "Array of read, write, admin" }, + { "expires_at", ARG_DATE, 0, NULL, NULL, "Expiry date" }, +}; + +static const struct cmd_arg args_token_revoke[] = { + { "id", ARG_INT, 1, NULL, NULL, "Token id" }, +}; + +static const struct cmd_arg args_describe[] = { + { "cmd", ARG_STR, 0, NULL, NULL, + "Command name; omit for the full catalogue" }, +}; + +static const struct cmd_arg args_audit_list[] = { + { "cursor", ARG_INT, 0, NULL, NULL, "Cursor from next_cursor" }, + { "limit", ARG_INT, 0, "100", NULL, "Page size, 1-1000" }, + { "action", ARG_STR, 0, NULL, NULL, "Filter by action" }, +}; + +static const struct cmd_arg args_audit_verify[] = { + { "full", ARG_BOOL, 0, "false", NULL, "Also re-hash attachments" }, +}; + +static const struct cmd_arg args_backup_snapshot[] = { + { "dest", ARG_STR, 0, NULL, NULL, + "Destination path; defaults to backup_dir with a timestamp" }, +}; + +static const struct cmd_arg args_account_list[] = { + { "active_only", ARG_BOOL, 0, NULL, NULL, "Only active accounts" }, +}; + +static const struct cmd_arg args_account_get[] = { + { "id", ARG_INT, 0, NULL, NULL, "Account id" }, + { "number", ARG_STR, 0, NULL, NULL, "Account number (string)" }, +}; + +static const struct cmd_arg args_account_create[] = { + { "number", ARG_STR, 1, NULL, NULL, "Account number, 1-10 digits" }, + { "name", ARG_STR, 1, NULL, NULL, "Account name" }, + { "type", ARG_ENUM, 1, NULL, "asset,liability,equity,revenue,expense", + "Account type" }, + { "sru_code", ARG_STR, 0, NULL, NULL, "SRU code" }, + { "vat_code", ARG_STR, 0, NULL, NULL, "VAT code" }, +}; + +static const struct cmd_arg args_account_update[] = { + { "id", ARG_INT, 1, NULL, NULL, "Account id" }, + { "name", ARG_STR, 0, NULL, NULL, "Account name" }, + { "sru_code", ARG_STR, 0, NULL, NULL, "SRU code" }, + { "vat_code", ARG_STR, 0, NULL, NULL, "VAT code" }, + { "active", ARG_BOOL, 0, NULL, NULL, "Active flag" }, +}; + +static const struct cmd_arg args_fiscal_year_get[] = { + { "id", ARG_INT, 0, NULL, NULL, + "Fiscal year id; defaults to the latest" }, +}; + +static const struct cmd_arg args_fiscal_year_open[] = { + { "label", ARG_STR, 1, NULL, NULL, "Fiscal year label" }, + { "start_date", ARG_DATE, 1, NULL, NULL, "Start date (YYYY-MM-DD)" }, + { "end_date", ARG_DATE, 1, NULL, NULL, "End date (YYYY-MM-DD)" }, +}; + +static const struct cmd_arg args_fiscal_year_close[] = { + { "id", ARG_INT, 1, NULL, NULL, "Fiscal year id" }, + { "confirm", ARG_BOOL, 1, NULL, NULL, "Must be true" }, +}; + +static const struct cmd_arg args_fiscal_year_reopen[] = { + { "id", ARG_INT, 1, NULL, NULL, "Fiscal year id" }, + { "confirm", ARG_BOOL, 1, NULL, NULL, "Must be true" }, +}; + +static const struct cmd_arg args_fiscal_year_update[] = { + { "id", ARG_INT, 1, NULL, NULL, "Fiscal year id" }, + { "dividend_ore", ARG_INT, 0, NULL, NULL, "Proposed dividend in öre" }, + { "events", ARG_STR, 0, NULL, NULL, "Material events during the year" }, + { "agm_date", ARG_STR, 0, NULL, NULL, "AGM date; empty clears" }, + { "dividend_date", ARG_STR, 0, NULL, NULL, + "Dividend payment date; empty clears" }, + { "employees", ARG_STR, 0, NULL, NULL, "Average number of employees" }, + { "notes", ARG_STR, 0, NULL, NULL, "Other notes" }, +}; + +static const struct cmd_arg args_period_lock[] = { + { "fiscal_year", ARG_INT, 1, NULL, NULL, "Fiscal year id" }, + { "until", ARG_DATE, 1, NULL, NULL, "Lock through this date, inclusive" }, + { "reason", ARG_STR, 0, NULL, NULL, "Reason, recorded in the audit log" }, +}; + +static const struct cmd_arg args_period_unlock[] = { + { "fiscal_year", ARG_INT, 1, NULL, NULL, "Fiscal year id" }, + { "reason", ARG_STR, 0, NULL, NULL, "Reason, recorded in the audit log" }, +}; + +static const struct cmd_arg args_voucher_post[] = { + { "date", ARG_DATE, 1, NULL, NULL, "Voucher date (YYYY-MM-DD)" }, + { "description", ARG_STR, 0, NULL, NULL, "Voucher text" }, + { "series", ARG_STR, 0, NULL, NULL, + "Number series; defaults to settings.default_series" }, + { "client_ref", ARG_STR, 0, NULL, NULL, + "Idempotency key, unique per org" }, + { "corrects_voucher", ARG_INT, 0, NULL, NULL, + "Voucher id this one corrects" }, + { "rows", ARG_JSON, 0, NULL, NULL, + "Array of {account,debit_ore,credit_ore,description?}" }, + { "template", ARG_JSON, 0, NULL, NULL, + "Template id or name; alternative to rows" }, + { "x", ARG_JSON, 0, NULL, NULL, "Template variable in kronor" }, + { "attachment_ids", ARG_JSON, 0, NULL, NULL, + "Array of attachment ids to link" }, +}; + +static const struct cmd_arg args_voucher_get[] = { + { "id", ARG_INT, 1, NULL, NULL, "Voucher id" }, +}; + +static const struct cmd_arg args_voucher_list[] = { + { "fiscal_year", ARG_INT, 0, NULL, NULL, + "Fiscal year id; defaults to the latest" }, + { "from", ARG_DATE, 0, NULL, NULL, "Earliest date" }, + { "to", ARG_DATE, 0, NULL, NULL, "Latest date" }, + { "series", ARG_STR, 0, NULL, NULL, "Number series filter" }, + { "account", ARG_STR, 0, NULL, NULL, "Account number filter" }, + { "text", ARG_STR, 0, NULL, NULL, "Description substring filter" }, + { "cursor", ARG_INT, 0, NULL, NULL, "Cursor from next_cursor" }, + { "limit", ARG_INT, 0, "100", NULL, "Page size, 1-1000" }, +}; + +static const struct cmd_arg args_voucher_correct[] = { + { "voucher", ARG_INT, 1, NULL, NULL, "Voucher id to correct" }, + { "description", ARG_STR, 1, NULL, NULL, "Correction text" }, + { "date", ARG_DATE, 0, NULL, NULL, "Defaults to the original date" }, + { "client_ref", ARG_STR, 0, NULL, NULL, "Idempotency key" }, +}; + +static const struct cmd_arg args_bokslut_post[] = { + { "fiscal_year", ARG_INT, 0, NULL, NULL, + "Fiscal year id; defaults to the latest" }, + { "entries", ARG_JSON, 0, NULL, NULL, + "Array of {debit_account,credit_account,amount_ore,description?}" }, + { "periodiseringsfond_ore", ARG_INT, 0, NULL, NULL, + "Periodiseringsfond in öre" }, + { "tax_rate", ARG_JSON, 0, "20.6", NULL, "Tax rate percent, 0-100" }, + { "dispose", ARG_BOOL, 0, "true", NULL, + "Post the result transfer to 8999/2099" }, + { "date", ARG_DATE, 0, NULL, NULL, + "Posting date; defaults to the fiscal year end" }, +}; + +static const struct cmd_arg args_settings_set[] = { + { "key", ARG_STR, 1, NULL, NULL, "default_series or attachment_dir" }, + { "value", ARG_STR, 1, NULL, NULL, "Setting value" }, +}; + +static const struct cmd_arg args_template_list[] = { + { "active_only", ARG_BOOL, 0, NULL, NULL, "Only active templates" }, +}; + +static const struct cmd_arg args_template_get[] = { + { "id", ARG_INT, 0, NULL, NULL, "Template id" }, + { "name", ARG_STR, 0, NULL, NULL, "Template name" }, +}; + +static const struct cmd_arg args_template_create[] = { + { "name", ARG_STR, 1, NULL, NULL, "Template name" }, + { "series", ARG_STR, 0, NULL, NULL, + "Number series; defaults to settings.default_series" }, + { "description", ARG_STR, 0, NULL, NULL, + "Voucher text; {x} is replaced with the amount" }, + { "rows", ARG_JSON, 1, NULL, NULL, + "Array of {account,formula,description?}" }, +}; + +static const struct cmd_arg args_template_update[] = { + { "id", ARG_INT, 0, NULL, NULL, "Template id" }, + { "name", ARG_STR, 0, NULL, NULL, + "Template name, or the new name when id is given" }, + { "series", ARG_STR, 0, NULL, NULL, "Number series" }, + { "description", ARG_STR, 0, NULL, NULL, "Voucher text" }, + { "active", ARG_BOOL, 0, NULL, NULL, "Active flag" }, + { "rows", ARG_JSON, 0, NULL, NULL, + "Replacement rows, {account,formula,description?}" }, +}; + +static const struct cmd_arg args_template_archive[] = { + { "id", ARG_INT, 0, NULL, NULL, "Template id" }, + { "name", ARG_STR, 0, NULL, NULL, "Template name" }, +}; + +static const struct cmd_arg args_attachment_put[] = { + { "filename", ARG_STR, 1, NULL, NULL, "File name" }, + { "mime", ARG_STR, 0, "application/octet-stream", NULL, "MIME type" }, + { "content_base64", ARG_STR, 1, NULL, NULL, "File content, base64" }, + { "voucher_id", ARG_INT, 0, NULL, NULL, "Link to this voucher" }, +}; + +static const struct cmd_arg args_attachment_link[] = { + { "id", ARG_INT, 1, NULL, NULL, "Attachment id" }, + { "voucher_id", ARG_INT, 1, NULL, NULL, "Voucher id" }, +}; + +static const struct cmd_arg args_attachment_unlink[] = { + { "id", ARG_INT, 1, NULL, NULL, "Attachment id" }, + { "voucher_id", ARG_INT, 1, NULL, NULL, "Voucher id" }, +}; + +static const struct cmd_arg args_attachment_get[] = { + { "id", ARG_INT, 1, NULL, NULL, "Attachment id" }, +}; + +static const struct cmd_arg args_attachment_list[] = { + { "voucher_id", ARG_INT, 0, NULL, NULL, + "Only attachments on this voucher" }, + { "unlinked", ARG_BOOL, 0, NULL, NULL, + "Only unlinked attachments (inbox)" }, + { "cursor", ARG_INT, 0, NULL, NULL, "Cursor from next_cursor" }, + { "limit", ARG_INT, 0, "100", NULL, "Page size, 1-1000" }, +}; + +static const struct cmd_arg args_report_trial_balance[] = { + { "fiscal_year", ARG_INT, 0, NULL, NULL, + "Fiscal year id; defaults to the latest" }, + { "from", ARG_DATE, 0, NULL, NULL, "Narrow the period" }, + { "to", ARG_DATE, 0, NULL, NULL, "Narrow the period" }, + { "include_zero", ARG_BOOL, 0, "false", NULL, + "Include accounts with no activity" }, +}; + +static const struct cmd_arg args_report_income_statement[] = { + { "fiscal_year", ARG_INT, 0, NULL, NULL, + "Fiscal year id; defaults to the latest" }, + { "from", ARG_DATE, 0, NULL, NULL, "Narrow the period" }, + { "to", ARG_DATE, 0, NULL, NULL, "Narrow the period" }, +}; + +static const struct cmd_arg args_report_balance_sheet[] = { + { "fiscal_year", ARG_INT, 0, NULL, NULL, + "Fiscal year id; defaults to the latest" }, + { "to", ARG_DATE, 0, NULL, NULL, "Report through this date" }, +}; + +static const struct cmd_arg args_report_vat[] = { + { "from", ARG_DATE, 1, NULL, NULL, "Period start" }, + { "to", ARG_DATE, 1, NULL, NULL, "Period end" }, +}; + +static const struct cmd_arg args_report_general_ledger[] = { + { "fiscal_year", ARG_INT, 0, NULL, NULL, + "Fiscal year id; defaults to the latest" }, + { "accounts", ARG_JSON, 0, NULL, NULL, + "Array of account numbers to include" }, + { "from", ARG_DATE, 0, NULL, NULL, "Narrow the period" }, + { "to", ARG_DATE, 0, NULL, NULL, "Narrow the period" }, +}; + +static const struct cmd_arg args_report_voucher_list[] = { + { "fiscal_year", ARG_INT, 0, NULL, NULL, + "Fiscal year id; defaults to the latest" }, + { "series", ARG_STR, 0, NULL, NULL, "Number series filter" }, +}; + +static const struct cmd_arg args_report_vat_eskd[] = { + { "from", ARG_DATE, 1, NULL, NULL, "Period start" }, + { "to", ARG_DATE, 1, NULL, NULL, "Period end" }, + { "upplysning", ARG_STR, 0, NULL, NULL, + "Free-text message to Skatteverket" }, +}; + +static const struct cmd_arg args_sru_export[] = { + { "fiscal_year", ARG_INT, 0, NULL, NULL, + "Fiscal year id; defaults to the latest" }, + { "adjustments", ARG_JSON, 0, NULL, NULL, + "Array of {code,amount_ore} tax adjustments" }, + { "submitter", ARG_JSON, 0, NULL, NULL, + "Object overriding the INFO.SRU submitter" }, + { "assisted", ARG_BOOL, 0, NULL, NULL, "Assisted declaration" }, + { "audited", ARG_BOOL, 0, NULL, NULL, "Audited declaration" }, + { "ignore_unmapped", ARG_BOOL, 0, "false", NULL, + "Proceed despite unmapped accounts" }, +}; + +static const struct cmd_arg args_sie_export[] = { + { "fiscal_year", ARG_INT, 0, NULL, NULL, + "Fiscal year id; defaults to the latest" }, + { "inline", ARG_BOOL, 0, "false", NULL, "Also return content_base64" }, +}; + +static const struct cmd_arg args_sie_import[] = { + { "content_base64", ARG_STR, 0, NULL, NULL, "SIE file content, base64" }, + { "path", ARG_STR, 0, NULL, NULL, + "Server-side path; alternative to content_base64" }, +}; + const struct command g_commands[] = { - { "health", "Liveness probe", PERM_PUBLIC, 0, 0, 0, h_health }, - { "meta", "Server metadata and limits", PERM_PUBLIC, 0, 0, 0, h_meta }, + { "health", "Liveness probe", PERM_PUBLIC, 0, 0, 0, h_health, NULL, 0 }, + { "meta", "Server metadata and limits", PERM_PUBLIC, 0, 0, 0, h_meta, NULL, + 0 }, { "session.open", "Open a session (password or token)", PERM_PUBLIC, 0, 0, - 0, h_session_open }, + 0, h_session_open, CMD_ARGS(args_session_open) }, { "session.close", "Close the current session", PERM_READ, 0, 0, 0, - h_session_close }, + h_session_close, NULL, 0 }, { "session.whoami", "Current user, org, role and scopes", PERM_READ, 0, 0, - 0, h_session_whoami }, + 0, h_session_whoami, NULL, 0 }, { "session.list_orgs", "Orgs the current user is a member of", PERM_READ, - 0, 0, 0, h_session_list_orgs }, + 0, 0, 0, h_session_list_orgs, NULL, 0 }, { "session.use_org", "Switch active org", PERM_READ, 0, 0, 0, - h_session_use_org }, + h_session_use_org, CMD_ARGS(args_session_use_org) }, { "org.create", "Create an org and become its owner", PERM_READ, 0, 1, 1, - h_org_create }, + h_org_create, CMD_ARGS(args_org_create) }, { "org.list", "List orgs for the current user", PERM_READ, 0, 0, 0, - h_org_list }, - { "org.get", "Get org details", PERM_READ, 1, 0, 0, h_org_get }, + h_org_list, NULL, 0 }, + { "org.get", "Get org details", PERM_READ, 1, 0, 0, h_org_get, NULL, 0 }, { "org.update", "Update org details (owner)", PERM_OWNER, 1, 1, 1, - h_org_update }, + h_org_update, CMD_ARGS(args_org_update) }, { "org.member_list", "List org members", PERM_READ, 1, 0, 0, - h_org_member_list }, + h_org_member_list, NULL, 0 }, { "org.member_add", "Add a member to the org", PERM_OWNER, 1, 1, 1, - h_org_member_add }, + h_org_member_add, CMD_ARGS(args_member_role) }, { "org.member_set_role", "Change a member's role", PERM_OWNER, 1, 1, 1, - h_org_member_set_role }, + h_org_member_set_role, CMD_ARGS(args_member_role) }, { "org.member_remove", "Remove a member from the org", PERM_OWNER, 1, 1, 1, - h_org_member_remove }, - { "board.list", "List board members", PERM_READ, 1, 0, 0, h_board_list }, - { "board.add", "Add a board member", PERM_OWNER, 1, 1, 1, h_board_add }, + h_org_member_remove, CMD_ARGS(args_member_username) }, + { "board.list", "List board members", PERM_READ, 1, 0, 0, h_board_list, + NULL, 0 }, + { "board.add", "Add a board member", PERM_OWNER, 1, 1, 1, h_board_add, + CMD_ARGS(args_board_add) }, { "board.update", "Update a board member", PERM_OWNER, 1, 1, 1, - h_board_update }, + h_board_update, CMD_ARGS(args_board_update) }, { "board.remove", "Remove a board member", PERM_OWNER, 1, 1, 1, - h_board_remove }, + h_board_remove, CMD_ARGS(args_board_remove) }, { "user.create", "Create a user (system admin)", PERM_ADMIN, 0, 1, 1, - h_user_create }, + h_user_create, CMD_ARGS(args_user_create) }, { "user.list", "List users (system admin)", PERM_ADMIN, 0, 0, 0, - h_user_list }, + h_user_list, NULL, 0 }, { "token.create", "Create an API token for the active org", PERM_READ, 1, - 1, 1, h_token_create }, + 1, 1, h_token_create, CMD_ARGS(args_token_create) }, { "token.list", "List your API tokens in the active org", PERM_READ, 1, 0, - 0, h_token_list }, + 0, h_token_list, NULL, 0 }, { "token.revoke", "Revoke an API token", PERM_READ, 1, 1, 1, - h_token_revoke }, + h_token_revoke, CMD_ARGS(args_token_revoke) }, { "describe", "List implemented commands and permissions", PERM_READ, 0, 0, - 0, h_describe }, + 0, h_describe, CMD_ARGS(args_describe) }, { "agent.instructions", "Workflow rules for agents", PERM_READ, 0, 0, 0, - h_agent_instructions }, + h_agent_instructions, NULL, 0 }, { "audit.list", "Read the audit log for the active org", PERM_READ, 1, 0, - 0, h_audit_list }, + 0, h_audit_list, CMD_ARGS(args_audit_list) }, { "audit.verify", "Verify the voucher and audit hash chains", PERM_READ, 0, - 0, 0, h_audit_verify }, + 0, 0, h_audit_verify, CMD_ARGS(args_audit_verify) }, { "backup.snapshot", "Create a consistent database snapshot", PERM_ADMIN, - 0, 1, 0, h_backup_snapshot }, + 0, 1, 0, h_backup_snapshot, CMD_ARGS(args_backup_snapshot) }, { "account.list", "List the chart of accounts", PERM_READ, 1, 0, 0, - h_account_list }, - { "account.get", "Get one account", PERM_READ, 1, 0, 0, h_account_get }, + h_account_list, CMD_ARGS(args_account_list) }, + { "account.get", "Get one account", PERM_READ, 1, 0, 0, h_account_get, + CMD_ARGS(args_account_get) }, { "account.create", "Add an account", PERM_WRITE, 1, 1, 1, - h_account_create }, + h_account_create, CMD_ARGS(args_account_create) }, { "account.update", "Rename or deactivate an account", PERM_WRITE, 1, 1, 1, - h_account_update }, + h_account_update, CMD_ARGS(args_account_update) }, { "fiscal_year.list", "List fiscal years", PERM_READ, 1, 0, 0, - h_fiscal_year_list }, + h_fiscal_year_list, NULL, 0 }, { "fiscal_year.get", "Get a fiscal year", PERM_READ, 1, 0, 0, - h_fiscal_year_get }, + h_fiscal_year_get, CMD_ARGS(args_fiscal_year_get) }, { "fiscal_year.open", "Open a new fiscal year", PERM_OWNER, 1, 1, 1, - h_fiscal_year_open }, + h_fiscal_year_open, CMD_ARGS(args_fiscal_year_open) }, { "fiscal_year.close", "Close a fiscal year", PERM_OWNER, 1, 1, 0, - h_fiscal_year_close }, + h_fiscal_year_close, CMD_ARGS(args_fiscal_year_close) }, { "fiscal_year.reopen", "Reopen a closed fiscal year", PERM_OWNER, 1, 1, 1, - h_fiscal_year_reopen }, + h_fiscal_year_reopen, CMD_ARGS(args_fiscal_year_reopen) }, { "fiscal_year.update", "Update fiscal-year metadata (dividend)", - PERM_WRITE, 1, 1, 1, h_fiscal_year_update }, + PERM_WRITE, 1, 1, 1, h_fiscal_year_update, + CMD_ARGS(args_fiscal_year_update) }, { "period.lock", "Lock a period through a date", PERM_OWNER, 1, 1, 1, - h_period_lock }, + h_period_lock, CMD_ARGS(args_period_lock) }, { "period.unlock", "Remove a period lock", PERM_OWNER, 1, 1, 1, - h_period_unlock }, + h_period_unlock, CMD_ARGS(args_period_unlock) }, { "voucher.post", "Post an immutable voucher (rows or template+x)", - PERM_WRITE, 1, 1, 1, h_voucher_post }, + PERM_WRITE, 1, 1, 1, h_voucher_post, CMD_ARGS(args_voucher_post) }, { "voucher.get", "Get a voucher with rows", PERM_READ, 1, 0, 0, - h_voucher_get }, - { "voucher.list", "List vouchers", PERM_READ, 1, 0, 0, h_voucher_list }, + h_voucher_get, CMD_ARGS(args_voucher_get) }, + { "voucher.list", "List vouchers", PERM_READ, 1, 0, 0, h_voucher_list, + CMD_ARGS(args_voucher_list) }, { "voucher.correct", "Post an ändringsverifikat", PERM_WRITE, 1, 1, 1, - h_voucher_correct }, + h_voucher_correct, CMD_ARGS(args_voucher_correct) }, { "bokslut.post", "Year-end: dispositions, tax and result transfer", - PERM_WRITE, 1, 1, 1, h_bokslut_post }, + PERM_WRITE, 1, 1, 1, h_bokslut_post, CMD_ARGS(args_bokslut_post) }, { "settings.get", "Read org settings", PERM_READ, 1, 0, 0, - h_settings_get }, + h_settings_get, NULL, 0 }, { "settings.set", "Change an org setting", PERM_WRITE, 1, 1, 1, - h_settings_set }, + h_settings_set, CMD_ARGS(args_settings_set) }, { "template.list", "List voucher templates", PERM_READ, 1, 0, 0, - h_template_list }, + h_template_list, CMD_ARGS(args_template_list) }, { "template.get", "Get a voucher template with its rows", PERM_READ, 1, 0, - 0, h_template_get }, + 0, h_template_get, CMD_ARGS(args_template_get) }, { "template.create", "Create a voucher template", PERM_WRITE, 1, 1, 1, - h_template_create }, + h_template_create, CMD_ARGS(args_template_create) }, { "template.update", "Update a voucher template", PERM_WRITE, 1, 1, 1, - h_template_update }, + h_template_update, CMD_ARGS(args_template_update) }, { "template.archive", "Archive a voucher template", PERM_WRITE, 1, 1, 1, - h_template_archive }, + h_template_archive, CMD_ARGS(args_template_archive) }, { "attachment.put", "Store an underlag (receipt, invoice)", PERM_WRITE, 1, - 1, 1, h_attachment_put }, + 1, 1, h_attachment_put, CMD_ARGS(args_attachment_put) }, { "attachment.link", "Link an existing attachment to a voucher", - PERM_WRITE, 1, 1, 1, h_attachment_link }, + PERM_WRITE, 1, 1, 1, h_attachment_link, CMD_ARGS(args_attachment_link) }, { "attachment.unlink", "Remove an attachment link", PERM_WRITE, 1, 1, 1, - h_attachment_unlink }, + h_attachment_unlink, CMD_ARGS(args_attachment_unlink) }, { "attachment.get", "Fetch an attachment", PERM_READ, 1, 0, 0, - h_attachment_get }, + h_attachment_get, CMD_ARGS(args_attachment_get) }, { "attachment.list", "List attachments / inbox", PERM_READ, 1, 0, 0, - h_attachment_list }, + h_attachment_list, CMD_ARGS(args_attachment_list) }, { "report.trial_balance", "Saldobalans (IB, period, UB)", PERM_READ, 1, 0, - 0, h_report_trial_balance }, + 0, h_report_trial_balance, CMD_ARGS(args_report_trial_balance) }, { "report.income_statement", "Resultaträkning", PERM_READ, 1, 0, 0, - h_report_income_statement }, + h_report_income_statement, CMD_ARGS(args_report_income_statement) }, { "report.balance_sheet", "Balansräkning", PERM_READ, 1, 0, 0, - h_report_balance_sheet }, + h_report_balance_sheet, CMD_ARGS(args_report_balance_sheet) }, { "report.vat", "Momsdeklaration ruta för ruta", PERM_READ, 1, 0, 0, - h_report_vat }, + h_report_vat, CMD_ARGS(args_report_vat) }, { "report.general_ledger", "Huvudbok", PERM_READ, 1, 0, 0, - h_report_general_ledger }, + h_report_general_ledger, CMD_ARGS(args_report_general_ledger) }, { "report.voucher_list", "Verifikationslista", PERM_READ, 1, 0, 0, - h_report_voucher_list }, + h_report_voucher_list, CMD_ARGS(args_report_voucher_list) }, { "report.vat_eskd", "eSKD XML for the momsdeklaration (ISO-8859-1)", - PERM_READ, 1, 0, 0, h_report_vat_eskd }, + PERM_READ, 1, 0, 0, h_report_vat_eskd, CMD_ARGS(args_report_vat_eskd) }, { "sru.export", "INK2 SRU files (INFO.SRU + BLANKETTER.SRU)", - PERM_READ, 1, 0, 0, h_sru_export }, - { "sie.export", "Export SIE 4 (PC8)", PERM_READ, 1, 0, 0, h_sie_export }, + PERM_READ, 1, 0, 0, h_sru_export, CMD_ARGS(args_sru_export) }, + { "sie.export", "Export SIE 4 (PC8)", PERM_READ, 1, 0, 0, h_sie_export, + CMD_ARGS(args_sie_export) }, { "sie.import", "Import SIE 4 into an empty fiscal year", PERM_WRITE, 1, 1, - 1, h_sie_import }, + 1, h_sie_import, CMD_ARGS(args_sie_import) }, }; const size_t g_commands_count = sizeof g_commands / sizeof g_commands[0]; -- cgit v1.3