mirror of
https://github.com/ElementsProject/lightning.git
synced 2025-03-11 01:27:58 +01:00
script: Update scripts
- Update `make doc-all` script - `fromschema.py` script - Updated to generate whole doc/lightning-*.md via doc/schemas/lightning-*.json file - Updated to print request params - Added `oneOfMany` condition for request params - Added `pairedWith` condition for request params - Added `dependentUpon` condition for request params - Updated for pre and post_return_value for response - Hiding `hidden` fields - `descriptions` are array now - Unified gaps between titles - Added default key-value pair - script: msggen, sql-schema, listconfig and pyln-testing script updates This does not add created markdowns for cleaner review. We will add the markdown files in a separate commit.
This commit is contained in:
parent
4c370022d0
commit
418ecf3e42
16 changed files with 869 additions and 634 deletions
17
.msggen.json
17
.msggen.json
|
@ -499,6 +499,9 @@
|
|||
"DatastoreUsage.datastoreusage.key": 1,
|
||||
"DatastoreUsage.datastoreusage.total_bytes": 2
|
||||
},
|
||||
"DatastoreusageRequest": {
|
||||
"DatastoreUsage.key": 1
|
||||
},
|
||||
"DatastoreusageResponse": {
|
||||
"DatastoreUsage.datastoreusage": 1
|
||||
},
|
||||
|
@ -1215,6 +1218,7 @@
|
|||
"ListPeerChannels.channels[].peer_id": 1,
|
||||
"ListPeerChannels.channels[].private": 18,
|
||||
"ListPeerChannels.channels[].receivable_msat": 34,
|
||||
"ListPeerChannels.channels[].reestablished": 58,
|
||||
"ListPeerChannels.channels[].scratch_txid": 4,
|
||||
"ListPeerChannels.channels[].short_channel_id": 8,
|
||||
"ListPeerChannels.channels[].spendable_msat": 33,
|
||||
|
@ -1415,6 +1419,7 @@
|
|||
"ListSendPays.payments[].amount_sent_msat": 8,
|
||||
"ListSendPays.payments[].bolt11": 10,
|
||||
"ListSendPays.payments[].bolt12": 11,
|
||||
"ListSendPays.payments[].completed_at": 18,
|
||||
"ListSendPays.payments[].created_at": 7,
|
||||
"ListSendPays.payments[].created_index": 16,
|
||||
"ListSendPays.payments[].description": 14,
|
||||
|
@ -2213,6 +2218,10 @@
|
|||
"added": "v23.11",
|
||||
"deprecated": false
|
||||
},
|
||||
"DatastoreUsage.key": {
|
||||
"added": "v23.11",
|
||||
"deprecated": false
|
||||
},
|
||||
"Decode": {
|
||||
"added": "v23.05",
|
||||
"deprecated": null
|
||||
|
@ -4693,6 +4702,10 @@
|
|||
"added": "v23.02",
|
||||
"deprecated": false
|
||||
},
|
||||
"ListPeerChannels.channels[].reestablished": {
|
||||
"added": "v24.02",
|
||||
"deprecated": false
|
||||
},
|
||||
"ListPeerChannels.channels[].scratch_txid": {
|
||||
"added": "v23.02",
|
||||
"deprecated": false
|
||||
|
@ -5241,6 +5254,10 @@
|
|||
"added": "pre-v0.10.1",
|
||||
"deprecated": false
|
||||
},
|
||||
"ListSendPays.payments[].completed_at": {
|
||||
"added": "pre-v0.10.1",
|
||||
"deprecated": false
|
||||
},
|
||||
"ListSendPays.payments[].created_at": {
|
||||
"added": "pre-v0.10.1",
|
||||
"deprecated": false
|
||||
|
|
7
cln-grpc/proto/node.proto
generated
7
cln-grpc/proto/node.proto
generated
|
@ -494,6 +494,7 @@ message ConnectAddress {
|
|||
|
||||
message CreateinvoiceRequest {
|
||||
string invstring = 1;
|
||||
string label = 2;
|
||||
bytes preimage = 3;
|
||||
}
|
||||
|
||||
|
@ -551,6 +552,7 @@ message DatastoreResponse {
|
|||
}
|
||||
|
||||
message DatastoreusageRequest {
|
||||
repeated string key = 1;
|
||||
}
|
||||
|
||||
message DatastoreusageResponse {
|
||||
|
@ -810,6 +812,7 @@ message ListsendpaysPayments {
|
|||
optional string bolt11 = 10;
|
||||
optional string description = 14;
|
||||
optional string bolt12 = 11;
|
||||
optional uint64 completed_at = 18;
|
||||
optional bytes payment_preimage = 12;
|
||||
optional bytes erroronion = 13;
|
||||
}
|
||||
|
@ -942,6 +945,7 @@ message WaitanyinvoicePaid_outpoint {
|
|||
}
|
||||
|
||||
message WaitinvoiceRequest {
|
||||
string label = 1;
|
||||
}
|
||||
|
||||
message WaitinvoiceResponse {
|
||||
|
@ -1198,6 +1202,7 @@ message ListpeerchannelsChannels {
|
|||
}
|
||||
optional bytes peer_id = 1;
|
||||
optional bool peer_connected = 2;
|
||||
optional bool reestablished = 58;
|
||||
optional ListpeerchannelsChannelsState state = 3;
|
||||
optional bytes scratch_txid = 4;
|
||||
optional ListpeerchannelsChannelsUpdates updates = 55;
|
||||
|
@ -1837,7 +1842,7 @@ message OfferRequest {
|
|||
optional string recurrence = 7;
|
||||
optional string recurrence_base = 8;
|
||||
optional string recurrence_paywindow = 9;
|
||||
optional uint64 recurrence_limit = 10;
|
||||
optional uint32 recurrence_limit = 10;
|
||||
optional bool single_use = 11;
|
||||
}
|
||||
|
||||
|
|
17
cln-grpc/src/convert.rs
generated
17
cln-grpc/src/convert.rs
generated
|
@ -658,6 +658,7 @@ impl From<responses::ListsendpaysPayments> for pb::ListsendpaysPayments {
|
|||
bolt11: c.bolt11, // Rule #2 for type string?
|
||||
description: c.description, // Rule #2 for type string?
|
||||
bolt12: c.bolt12, // Rule #2 for type string?
|
||||
completed_at: c.completed_at, // Rule #2 for type u64?
|
||||
payment_preimage: c.payment_preimage.map(|v| v.to_vec()), // Rule #2 for type secret?
|
||||
erroronion: c.erroronion.map(|v| hex::decode(v).unwrap()), // Rule #2 for type hex?
|
||||
}
|
||||
|
@ -1118,6 +1119,7 @@ impl From<responses::ListpeerchannelsChannels> for pb::ListpeerchannelsChannels
|
|||
Self {
|
||||
peer_id: c.peer_id.map(|v| v.serialize().to_vec()), // Rule #2 for type pubkey?
|
||||
peer_connected: c.peer_connected, // Rule #2 for type boolean?
|
||||
reestablished: c.reestablished, // Rule #2 for type boolean?
|
||||
state: c.state.map(|v| v as i32),
|
||||
scratch_txid: c.scratch_txid.map(|v| hex::decode(v).unwrap()), // Rule #2 for type txid?
|
||||
updates: c.updates.map(|v| v.into()),
|
||||
|
@ -1576,7 +1578,7 @@ impl From<responses::FundchannelResponse> for pb::FundchannelResponse {
|
|||
tx: hex::decode(&c.tx).unwrap(), // Rule #2 for type hex
|
||||
txid: hex::decode(&c.txid).unwrap(), // Rule #2 for type txid
|
||||
outnum: c.outnum, // Rule #2 for type u32
|
||||
channel_id: hex::decode(&c.channel_id).unwrap(), // Rule #2 for type hex
|
||||
channel_id: <Sha256 as AsRef<[u8]>>::as_ref(&c.channel_id).to_vec(), // Rule #2 for type hash
|
||||
channel_type: c.channel_type.map(|v| v.into()),
|
||||
close_to: c.close_to.map(|v| hex::decode(v).unwrap()), // Rule #2 for type hex?
|
||||
mindepth: c.mindepth, // Rule #2 for type u32?
|
||||
|
@ -1757,7 +1759,7 @@ impl From<responses::SetchannelChannels> for pb::SetchannelChannels {
|
|||
fn from(c: responses::SetchannelChannels) -> Self {
|
||||
Self {
|
||||
peer_id: c.peer_id.serialize().to_vec(), // Rule #2 for type pubkey
|
||||
channel_id: hex::decode(&c.channel_id).unwrap(), // Rule #2 for type hex
|
||||
channel_id: <Sha256 as AsRef<[u8]>>::as_ref(&c.channel_id).to_vec(), // Rule #2 for type hash
|
||||
short_channel_id: c.short_channel_id.map(|v| v.to_string()), // Rule #2 for type short_channel_id?
|
||||
fee_base_msat: Some(c.fee_base_msat.into()), // Rule #2 for type msat
|
||||
fee_proportional_millionths: c.fee_proportional_millionths, // Rule #2 for type u32
|
||||
|
@ -2016,6 +2018,7 @@ impl From<requests::CreateinvoiceRequest> for pb::CreateinvoiceRequest {
|
|||
fn from(c: requests::CreateinvoiceRequest) -> Self {
|
||||
Self {
|
||||
invstring: c.invstring, // Rule #2 for type string
|
||||
label: c.label, // Rule #2 for type string
|
||||
preimage: hex::decode(&c.preimage).unwrap(), // Rule #2 for type hex
|
||||
}
|
||||
}
|
||||
|
@ -2039,6 +2042,8 @@ impl From<requests::DatastoreRequest> for pb::DatastoreRequest {
|
|||
impl From<requests::DatastoreusageRequest> for pb::DatastoreusageRequest {
|
||||
fn from(c: requests::DatastoreusageRequest) -> Self {
|
||||
Self {
|
||||
// Field: DatastoreUsage.key
|
||||
key: c.key.map(|arr| arr.into_iter().map(|i| i.into()).collect()).unwrap_or(vec![]), // Rule #3
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2237,6 +2242,7 @@ impl From<requests::WaitanyinvoiceRequest> for pb::WaitanyinvoiceRequest {
|
|||
impl From<requests::WaitinvoiceRequest> for pb::WaitinvoiceRequest {
|
||||
fn from(c: requests::WaitinvoiceRequest) -> Self {
|
||||
Self {
|
||||
label: c.label, // Rule #2 for type string
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2552,7 +2558,7 @@ impl From<requests::OfferRequest> for pb::OfferRequest {
|
|||
recurrence: c.recurrence, // Rule #2 for type string?
|
||||
recurrence_base: c.recurrence_base, // Rule #2 for type string?
|
||||
recurrence_paywindow: c.recurrence_paywindow, // Rule #2 for type string?
|
||||
recurrence_limit: c.recurrence_limit, // Rule #2 for type u64?
|
||||
recurrence_limit: c.recurrence_limit, // Rule #2 for type u32?
|
||||
single_use: c.single_use, // Rule #2 for type boolean?
|
||||
}
|
||||
}
|
||||
|
@ -2811,6 +2817,7 @@ impl From<pb::CreateinvoiceRequest> for requests::CreateinvoiceRequest {
|
|||
fn from(c: pb::CreateinvoiceRequest) -> Self {
|
||||
Self {
|
||||
invstring: c.invstring, // Rule #1 for type string
|
||||
label: c.label, // Rule #1 for type string
|
||||
preimage: hex::encode(&c.preimage), // Rule #1 for type hex
|
||||
}
|
||||
}
|
||||
|
@ -2833,6 +2840,7 @@ impl From<pb::DatastoreRequest> for requests::DatastoreRequest {
|
|||
impl From<pb::DatastoreusageRequest> for requests::DatastoreusageRequest {
|
||||
fn from(c: pb::DatastoreusageRequest) -> Self {
|
||||
Self {
|
||||
key: Some(c.key.into_iter().map(|s| s.into()).collect()), // Rule #4
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -3025,6 +3033,7 @@ impl From<pb::WaitanyinvoiceRequest> for requests::WaitanyinvoiceRequest {
|
|||
impl From<pb::WaitinvoiceRequest> for requests::WaitinvoiceRequest {
|
||||
fn from(c: pb::WaitinvoiceRequest) -> Self {
|
||||
Self {
|
||||
label: c.label, // Rule #1 for type string
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -3332,7 +3341,7 @@ impl From<pb::OfferRequest> for requests::OfferRequest {
|
|||
recurrence: c.recurrence, // Rule #1 for type string?
|
||||
recurrence_base: c.recurrence_base, // Rule #1 for type string?
|
||||
recurrence_paywindow: c.recurrence_paywindow, // Rule #1 for type string?
|
||||
recurrence_limit: c.recurrence_limit, // Rule #1 for type u64?
|
||||
recurrence_limit: c.recurrence_limit, // Rule #1 for type u32?
|
||||
single_use: c.single_use, // Rule #1 for type boolean?
|
||||
}
|
||||
}
|
||||
|
|
98
cln-rpc/src/model.rs
generated
98
cln-rpc/src/model.rs
generated
|
@ -195,7 +195,7 @@ pub mod requests {
|
|||
"getinfo"
|
||||
}
|
||||
}
|
||||
/// supplying level will show log entries related to that peer at the given log level
|
||||
/// ['Supplying level will show log entries related to that peer at the given log level.']
|
||||
#[derive(Copy, Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
|
||||
pub enum ListpeersLevel {
|
||||
#[serde(rename = "io")]
|
||||
|
@ -491,6 +491,7 @@ pub mod requests {
|
|||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||
pub struct CreateinvoiceRequest {
|
||||
pub invstring: String,
|
||||
pub label: String,
|
||||
pub preimage: String,
|
||||
}
|
||||
|
||||
|
@ -511,7 +512,7 @@ pub mod requests {
|
|||
"createinvoice"
|
||||
}
|
||||
}
|
||||
/// - `must-create`: fails if it already exists. - `must-replace`: fails if it doesn't already exist. - `create-or-replace`: never fails. - `must-append`: must already exist, append this to what's already there. - `create-or-append`: append if anything is there, otherwise create. Default is `must-create`
|
||||
/// ['Write mode to determine how the record is updated:', ' * `must-create`: fails if it already exists.', " * `must-replace`: fails if it doesn't already exist.", ' * `create-or-replace`: never fails.', " * `must-append`: must already exist, append this to what's already there.", ' * `create-or-append`: append if anything is there, otherwise create.']
|
||||
#[derive(Copy, Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
|
||||
pub enum DatastoreMode {
|
||||
#[serde(rename = "must-create")]
|
||||
|
@ -584,6 +585,8 @@ pub mod requests {
|
|||
}
|
||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||
pub struct DatastoreusageRequest {
|
||||
#[serde(skip_serializing_if = "crate::is_none_or_empty")]
|
||||
pub key: Option<Vec<String>>,
|
||||
}
|
||||
|
||||
impl From<DatastoreusageRequest> for Request {
|
||||
|
@ -683,7 +686,7 @@ pub mod requests {
|
|||
"delexpiredinvoice"
|
||||
}
|
||||
}
|
||||
/// label of the invoice to be deleted. The caller should be particularly aware of the error case caused by the *status* changing just before this command is invoked!
|
||||
/// ['Label of the invoice to be deleted. The caller should be particularly aware of the error case caused by the *status* changing just before this command is invoked!']
|
||||
#[derive(Copy, Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
|
||||
pub enum DelinvoiceStatus {
|
||||
#[serde(rename = "paid")]
|
||||
|
@ -799,7 +802,7 @@ pub mod requests {
|
|||
"listdatastore"
|
||||
}
|
||||
}
|
||||
/// if neither *in_channel* nor *out_channel* is specified, it controls ordering. Defaults to `created`
|
||||
/// ['If neither *in_channel* nor *out_channel* is specified, it controls ordering.']
|
||||
#[derive(Copy, Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
|
||||
pub enum ListinvoicesIndex {
|
||||
#[serde(rename = "created")]
|
||||
|
@ -912,7 +915,7 @@ pub mod requests {
|
|||
"sendonion"
|
||||
}
|
||||
}
|
||||
/// Whether the invoice has been paid, pending, or failed
|
||||
/// ['Whether the invoice has been paid, pending, or failed.']
|
||||
#[derive(Copy, Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
|
||||
pub enum ListsendpaysStatus {
|
||||
#[serde(rename = "pending")]
|
||||
|
@ -945,7 +948,7 @@ pub mod requests {
|
|||
}
|
||||
}
|
||||
|
||||
/// if neither bolt11 or payment_hash is specified, `index` controls ordering, by `created` (default) or `updated`
|
||||
/// ['If neither bolt11 or payment_hash is specified, `index` controls ordering, by `created` (default) or `updated`.']
|
||||
#[derive(Copy, Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
|
||||
pub enum ListsendpaysIndex {
|
||||
#[serde(rename = "created")]
|
||||
|
@ -1122,6 +1125,7 @@ pub mod requests {
|
|||
}
|
||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||
pub struct WaitinvoiceRequest {
|
||||
pub label: String,
|
||||
}
|
||||
|
||||
impl From<WaitinvoiceRequest> for Request {
|
||||
|
@ -1169,7 +1173,7 @@ pub mod requests {
|
|||
"waitsendpay"
|
||||
}
|
||||
}
|
||||
/// it specifies the type of address wanted; currently *bech32* (e.g. `tb1qu9j4lg5f9rgjyfhvfd905vw46eg39czmktxqgg` on bitcoin testnet or `bc1qwqdg6squsna38e46795at95yu9atm8azzmyvckulcc7kytlcckxswvvzej` on bitcoin mainnet), or *p2tr* taproot addresses. The special value *all* generates all known address types for the same underlying key. Defaults to *bech32* address
|
||||
/// ['It specifies the type of address wanted; currently *bech32* (e.g. `tb1qu9j4lg5f9rgjyfhvfd905vw46eg39czmktxqgg` on bitcoin testnet or `bc1qwqdg6squsna38e46795at95yu9atm8azzmyvckulcc7kytlcckxswvvzej` on bitcoin mainnet), or *p2tr* taproot addresses. The special value *all* generates all known address types for the same underlying key.']
|
||||
#[derive(Copy, Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
|
||||
pub enum NewaddrAddresstype {
|
||||
#[serde(rename = "bech32")]
|
||||
|
@ -1602,7 +1606,7 @@ pub mod requests {
|
|||
"disconnect"
|
||||
}
|
||||
}
|
||||
/// *perkw* - provide feerate in units of satoshis per 1000 weight (e.g. the minimum fee is usually `253perkw`) *perkb* - provide feerate in units of satoshis per 1000 virtual bytes (eg. the minimum fee is usually `1000perkb`)
|
||||
/// ['Fee rate style to use. This can be:', ' *perkw* - provide feerate in units of satoshis per 1000 weight (e.g. the minimum fee is usually `253perkw`).', ' *perkb* - provide feerate in units of satoshis per 1000 virtual bytes (eg. the minimum fee is usually `1000perkb`).']
|
||||
#[derive(Copy, Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
|
||||
pub enum FeeratesStyle {
|
||||
#[serde(rename = "perkb")]
|
||||
|
@ -1769,7 +1773,7 @@ pub mod requests {
|
|||
"getroute"
|
||||
}
|
||||
}
|
||||
/// if specified, then only the forwards with the given status are returned
|
||||
/// ['If specified, then only the forwards with the given status are returned.']
|
||||
#[derive(Copy, Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
|
||||
pub enum ListforwardsStatus {
|
||||
#[serde(rename = "offered")]
|
||||
|
@ -1806,7 +1810,7 @@ pub mod requests {
|
|||
}
|
||||
}
|
||||
|
||||
/// if neither *in_channel* nor *out_channel* is specified, it controls ordering. Defaults to `created`
|
||||
/// ['If neither *in_channel* nor *out_channel* is specified, it controls ordering.']
|
||||
#[derive(Copy, Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
|
||||
pub enum ListforwardsIndex {
|
||||
#[serde(rename = "created")]
|
||||
|
@ -1893,7 +1897,7 @@ pub mod requests {
|
|||
"listoffers"
|
||||
}
|
||||
}
|
||||
/// to filter the payment by status
|
||||
/// ['To filter the payment by status.']
|
||||
#[derive(Copy, Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
|
||||
pub enum ListpaysStatus {
|
||||
#[serde(rename = "pending")]
|
||||
|
@ -1995,7 +1999,7 @@ pub mod requests {
|
|||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub recurrence_paywindow: Option<String>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub recurrence_limit: Option<u64>,
|
||||
pub recurrence_limit: Option<u32>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub single_use: Option<bool>,
|
||||
}
|
||||
|
@ -2168,7 +2172,7 @@ pub mod requests {
|
|||
"waitblockheight"
|
||||
}
|
||||
}
|
||||
/// the subsystem to get the next index value from
|
||||
/// ['The subsystem to get the next index value from.', ' `invoices`: corresponding to `listinvoices` (added in *v23.08*).', ' `sendpays`: corresponding to `listsendpays` (added in *v23.11*).', ' `forwards`: corresponding to `listforwards` (added in *v23.11*).']
|
||||
#[derive(Copy, Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
|
||||
pub enum WaitSubsystem {
|
||||
#[serde(rename = "invoices")]
|
||||
|
@ -2201,7 +2205,7 @@ pub mod requests {
|
|||
}
|
||||
}
|
||||
|
||||
/// the name of the index to get the next value for
|
||||
/// ['The name of the index to get the next value for.', ' `created` is incremented by one for every new object.', ' `updated` is incremented by one every time an object is changed.', ' `deleted` is incremented by one every time an object is deleted.']
|
||||
#[derive(Copy, Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
|
||||
pub enum WaitIndexname {
|
||||
#[serde(rename = "created")]
|
||||
|
@ -2397,7 +2401,7 @@ pub mod responses {
|
|||
pub invoice: String,
|
||||
}
|
||||
|
||||
/// Type of connection (until 23.08, `websocket` was also allowed)
|
||||
/// ['Type of connection (until 23.08, `websocket` was also allowed).']
|
||||
#[derive(Copy, Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
|
||||
pub enum GetinfoAddressType {
|
||||
#[serde(rename = "dns")]
|
||||
|
@ -2448,7 +2452,7 @@ pub mod responses {
|
|||
pub address: Option<String>,
|
||||
}
|
||||
|
||||
/// Type of connection
|
||||
/// ['Type of connection.']
|
||||
#[derive(Copy, Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
|
||||
pub enum GetinfoBindingType {
|
||||
#[serde(rename = "local socket")]
|
||||
|
@ -2612,7 +2616,7 @@ pub mod responses {
|
|||
pub data: Option<String>,
|
||||
}
|
||||
|
||||
/// the channel state, in particular "CHANNELD_NORMAL" means the channel can be used normally
|
||||
/// ['Current state of the channel:', ' * `OPENINGD`: The channel funding protocol with the peer is ongoing and both sides are negotiating parameters.', ' * `CHANNELD_AWAITING_LOCKIN`: The peer and you have agreed on channel parameters and are just waiting for the channel funding transaction to be confirmed deeply. Both you and the peer must acknowledge the channel funding transaction to be confirmed deeply before entering the next state.', ' * `CHANNELD_NORMAL`: The channel can be used for normal payments.', ' * `CHANNELD_SHUTTING_DOWN`: A mutual close was requested (by you or peer) and both of you are waiting for HTLCs in-flight to be either failed or succeeded. The channel can no longer be used for normal payments and forwarding. Mutual close will proceed only once all HTLCs in the channel have either been fulfilled or failed.', ' * `CLOSINGD_SIGEXCHANGE`: You and the peer are negotiating the mutual close onchain fee.', ' * `CLOSINGD_COMPLETE`: You and the peer have agreed on the mutual close onchain fee and are awaiting the mutual close getting confirmed deeply.', ' * `AWAITING_UNILATERAL`: You initiated a unilateral close, and are now waiting for the peer-selected unilateral close timeout to complete.', ' * `FUNDING_SPEND_SEEN`: You saw the funding transaction getting spent (usually the peer initiated a unilateral close) and will now determine what exactly happened (i.e. if it was a theft attempt).', ' * `ONCHAIN`: You saw the funding transaction getting spent and now know what happened (i.e. if it was a proper unilateral close by the peer, or a theft attempt).', ' * `CLOSED`: The channel closure has been confirmed deeply. The channel will eventually be removed from this array.']
|
||||
#[derive(Copy, Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
|
||||
pub enum ListpeersPeersChannelsState {
|
||||
#[serde(rename = "OPENINGD")]
|
||||
|
@ -2723,7 +2727,7 @@ pub mod responses {
|
|||
pub remote: Option<ShortChannelId>,
|
||||
}
|
||||
|
||||
/// Whether it came from peer, or is going to peer
|
||||
/// ['Whether it came from peer, or is going to peer.']
|
||||
#[derive(Copy, Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
|
||||
pub enum ListpeersPeersChannelsHtlcsDirection {
|
||||
#[serde(rename = "in")]
|
||||
|
@ -2990,7 +2994,7 @@ pub mod responses {
|
|||
}
|
||||
}
|
||||
|
||||
/// status of the payment (could be complete if already sent previously)
|
||||
/// ['Status of the payment (could be complete if already sent previously).']
|
||||
#[derive(Copy, Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
|
||||
pub enum SendpayStatus {
|
||||
#[serde(rename = "pending")]
|
||||
|
@ -3153,7 +3157,7 @@ pub mod responses {
|
|||
}
|
||||
}
|
||||
|
||||
/// Whether we successfully negotiated a mutual close, closed without them, or discarded not-yet-opened channel
|
||||
/// ['Whether we successfully negotiated a mutual close, closed without them, or discarded not-yet-opened channel.']
|
||||
#[derive(Copy, Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
|
||||
pub enum CloseType {
|
||||
#[serde(rename = "mutual")]
|
||||
|
@ -3208,7 +3212,7 @@ pub mod responses {
|
|||
}
|
||||
}
|
||||
|
||||
/// Whether they initiated connection or we did
|
||||
/// ['Whether they initiated connection or we did.']
|
||||
#[derive(Copy, Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
|
||||
pub enum ConnectDirection {
|
||||
#[serde(rename = "in")]
|
||||
|
@ -3237,7 +3241,7 @@ pub mod responses {
|
|||
}
|
||||
}
|
||||
|
||||
/// Type of connection (*torv2*/*torv3* only if **direction** is *out*)
|
||||
/// ['Type of connection (*torv2*/*torv3* only if **direction** is *out*).']
|
||||
#[derive(Copy, Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
|
||||
pub enum ConnectAddressType {
|
||||
#[serde(rename = "local socket")]
|
||||
|
@ -3311,7 +3315,7 @@ pub mod responses {
|
|||
}
|
||||
}
|
||||
|
||||
/// Whether it has been paid, or can no longer be paid
|
||||
/// ['Whether it has been paid, or can no longer be paid.']
|
||||
#[derive(Copy, Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
|
||||
pub enum CreateinvoiceStatus {
|
||||
#[serde(rename = "paid")]
|
||||
|
@ -3496,7 +3500,7 @@ pub mod responses {
|
|||
}
|
||||
}
|
||||
|
||||
/// State of invoice
|
||||
/// ['State of invoice.']
|
||||
#[derive(Copy, Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
|
||||
pub enum DelinvoiceStatus {
|
||||
#[serde(rename = "paid")]
|
||||
|
@ -3623,7 +3627,7 @@ pub mod responses {
|
|||
}
|
||||
}
|
||||
|
||||
/// Whether it's paid, unpaid or unpayable
|
||||
/// ["Whether it's paid, unpaid or unpayable."]
|
||||
#[derive(Copy, Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
|
||||
pub enum ListinvoicesInvoicesStatus {
|
||||
#[serde(rename = "unpaid")]
|
||||
|
@ -3715,7 +3719,7 @@ pub mod responses {
|
|||
}
|
||||
}
|
||||
|
||||
/// status of the payment (could be complete if already sent previously)
|
||||
/// ['Status of the payment (could be complete if already sent previously).']
|
||||
#[derive(Copy, Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
|
||||
pub enum SendonionStatus {
|
||||
#[serde(rename = "pending")]
|
||||
|
@ -3785,7 +3789,7 @@ pub mod responses {
|
|||
}
|
||||
}
|
||||
|
||||
/// status of the payment
|
||||
/// ['Status of the payment.']
|
||||
#[derive(Copy, Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
|
||||
pub enum ListsendpaysPaymentsStatus {
|
||||
#[serde(rename = "pending")]
|
||||
|
@ -3846,6 +3850,8 @@ pub mod responses {
|
|||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub bolt12: Option<String>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub completed_at: Option<u64>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub payment_preimage: Option<Secret>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub erroronion: Option<String>,
|
||||
|
@ -3910,7 +3916,7 @@ pub mod responses {
|
|||
}
|
||||
}
|
||||
|
||||
/// status of payment
|
||||
/// ['Status of payment.']
|
||||
#[derive(Copy, Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
|
||||
pub enum PayStatus {
|
||||
#[serde(rename = "complete")]
|
||||
|
@ -3970,7 +3976,7 @@ pub mod responses {
|
|||
}
|
||||
}
|
||||
|
||||
/// Type of connection (until 23.08, `websocket` was also allowed)
|
||||
/// ['Type of connection (until 23.08, `websocket` was also allowed).']
|
||||
#[derive(Copy, Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
|
||||
pub enum ListnodesNodesAddressesType {
|
||||
#[serde(rename = "dns")]
|
||||
|
@ -4052,7 +4058,7 @@ pub mod responses {
|
|||
}
|
||||
}
|
||||
|
||||
/// Whether it's paid or expired
|
||||
/// ["Whether it's paid or expired."]
|
||||
#[derive(Copy, Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
|
||||
pub enum WaitanyinvoiceStatus {
|
||||
#[serde(rename = "paid")]
|
||||
|
@ -4130,7 +4136,7 @@ pub mod responses {
|
|||
}
|
||||
}
|
||||
|
||||
/// Whether it's paid or expired
|
||||
/// ["Whether it's paid or expired."]
|
||||
#[derive(Copy, Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
|
||||
pub enum WaitinvoiceStatus {
|
||||
#[serde(rename = "paid")]
|
||||
|
@ -4208,7 +4214,7 @@ pub mod responses {
|
|||
}
|
||||
}
|
||||
|
||||
/// status of the payment
|
||||
/// ['Status of the payment.']
|
||||
#[derive(Copy, Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
|
||||
pub enum WaitsendpayStatus {
|
||||
#[serde(rename = "complete")]
|
||||
|
@ -4313,7 +4319,7 @@ pub mod responses {
|
|||
}
|
||||
}
|
||||
|
||||
/// status of payment
|
||||
/// ['Status of payment.']
|
||||
#[derive(Copy, Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
|
||||
pub enum KeysendStatus {
|
||||
#[serde(rename = "complete")]
|
||||
|
@ -4515,7 +4521,7 @@ pub mod responses {
|
|||
}
|
||||
}
|
||||
|
||||
/// the channel state, in particular "CHANNELD_NORMAL" means the channel can be used normally
|
||||
/// ['The channel state, in particular `CHANNELD_NORMAL` means the channel can be used normally.']
|
||||
#[derive(Copy, Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
|
||||
pub enum ListpeerchannelsChannelsState {
|
||||
#[serde(rename = "OPENINGD")]
|
||||
|
@ -4676,7 +4682,7 @@ pub mod responses {
|
|||
pub remote: Option<ShortChannelId>,
|
||||
}
|
||||
|
||||
/// Whether it came from peer, or is going to peer
|
||||
/// ['Whether it came from peer, or is going to peer.']
|
||||
#[derive(Copy, Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
|
||||
pub enum ListpeerchannelsChannelsHtlcsDirection {
|
||||
#[serde(rename = "in")]
|
||||
|
@ -4732,6 +4738,8 @@ pub mod responses {
|
|||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub peer_connected: Option<bool>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub reestablished: Option<bool>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub state: Option<ListpeerchannelsChannelsState>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub scratch_txid: Option<String>,
|
||||
|
@ -4862,7 +4870,7 @@ pub mod responses {
|
|||
pub remote: Option<ShortChannelId>,
|
||||
}
|
||||
|
||||
/// What caused the channel to close
|
||||
/// ['What caused the channel to close.']
|
||||
#[derive(Copy, Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
|
||||
pub enum ListclosedchannelsClosedchannelsClose_cause {
|
||||
#[serde(rename = "unknown")]
|
||||
|
@ -4963,7 +4971,7 @@ pub mod responses {
|
|||
}
|
||||
}
|
||||
|
||||
/// the address type (if known)
|
||||
/// ['The address type (if known).']
|
||||
#[derive(Copy, Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
|
||||
pub enum DecodepayFallbacksType {
|
||||
#[serde(rename = "P2PKH")]
|
||||
|
@ -5058,7 +5066,7 @@ pub mod responses {
|
|||
}
|
||||
}
|
||||
|
||||
/// what kind of object it decoded to
|
||||
/// ['What kind of object it decoded to.']
|
||||
#[derive(Copy, Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
|
||||
pub enum DecodeType {
|
||||
#[serde(rename = "bolt12 offer")]
|
||||
|
@ -5466,7 +5474,7 @@ pub mod responses {
|
|||
pub tx: String,
|
||||
pub txid: String,
|
||||
pub outnum: u32,
|
||||
pub channel_id: String,
|
||||
pub channel_id: Sha256,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub channel_type: Option<FundchannelChannel_type>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
|
@ -5486,7 +5494,7 @@ pub mod responses {
|
|||
}
|
||||
}
|
||||
|
||||
/// The features understood by the destination node
|
||||
/// ['The features understood by the destination node.']
|
||||
#[derive(Copy, Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
|
||||
pub enum GetrouteRouteStyle {
|
||||
#[serde(rename = "tlv")]
|
||||
|
@ -5538,7 +5546,7 @@ pub mod responses {
|
|||
}
|
||||
}
|
||||
|
||||
/// still ongoing, completed, failed locally, or failed after forwarding
|
||||
/// ['Still ongoing, completed, failed locally, or failed after forwarding.']
|
||||
#[derive(Copy, Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
|
||||
pub enum ListforwardsForwardsStatus {
|
||||
#[serde(rename = "offered")]
|
||||
|
@ -5575,7 +5583,7 @@ pub mod responses {
|
|||
}
|
||||
}
|
||||
|
||||
/// Either a legacy onion format or a modern tlv format
|
||||
/// ['Either a legacy onion format or a modern tlv format.']
|
||||
#[derive(Copy, Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
|
||||
pub enum ListforwardsForwardsStyle {
|
||||
#[serde(rename = "legacy")]
|
||||
|
@ -5672,7 +5680,7 @@ pub mod responses {
|
|||
}
|
||||
}
|
||||
|
||||
/// status of the payment
|
||||
/// ['Status of the payment.']
|
||||
#[derive(Copy, Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
|
||||
pub enum ListpaysPaysStatus {
|
||||
#[serde(rename = "pending")]
|
||||
|
@ -5751,7 +5759,7 @@ pub mod responses {
|
|||
}
|
||||
}
|
||||
|
||||
/// out if we offered this to the peer, in if they offered it
|
||||
/// ['Out if we offered this to the peer, in if they offered it.']
|
||||
#[derive(Copy, Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
|
||||
pub enum ListhtlcsHtlcsDirection {
|
||||
#[serde(rename = "out")]
|
||||
|
@ -5867,7 +5875,7 @@ pub mod responses {
|
|||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||
pub struct SetchannelChannels {
|
||||
pub peer_id: PublicKey,
|
||||
pub channel_id: String,
|
||||
pub channel_id: Sha256,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub short_channel_id: Option<ShortChannelId>,
|
||||
pub fee_base_msat: Amount,
|
||||
|
|
|
@ -13,6 +13,7 @@ typemap = {
|
|||
'msat': 'Amount',
|
||||
'msat_or_all': 'AmountOrAll',
|
||||
'msat_or_any': 'AmountOrAny',
|
||||
'currency': 'string',
|
||||
'number': 'double',
|
||||
'pubkey': 'bytes',
|
||||
'short_channel_id': 'string',
|
||||
|
@ -33,6 +34,7 @@ typemap = {
|
|||
"feerate": "Feerate",
|
||||
"outputdesc": "OutputDesc",
|
||||
"secret": "bytes",
|
||||
"bip340sig": "bytes",
|
||||
"hash": "bytes",
|
||||
}
|
||||
|
||||
|
|
|
@ -35,6 +35,7 @@ class Grpc2PyGenerator(IGenerator):
|
|||
'signature': "hexlify(m.{name})",
|
||||
'txid': "hexlify(m.{name})",
|
||||
'hash': "hexlify(m.{name})",
|
||||
'bip340sig': "hexlify(m.{name})",
|
||||
'string': "m.{name}",
|
||||
'u8': "m.{name}",
|
||||
'u16': "m.{name}",
|
||||
|
@ -48,6 +49,7 @@ class Grpc2PyGenerator(IGenerator):
|
|||
'boolean': "m.{name}",
|
||||
'short_channel_id': "m.{name}",
|
||||
'msat': "amount2msat(m.{name})",
|
||||
'currency': "m.{name}",
|
||||
'number': "m.{name}",
|
||||
}
|
||||
|
||||
|
|
|
@ -22,6 +22,7 @@ typemap = {
|
|||
'msat': 'Amount',
|
||||
'msat_or_all': 'AmountOrAll',
|
||||
'msat_or_any': 'AmountOrAny',
|
||||
'currency': 'String',
|
||||
'number': 'f64',
|
||||
'pubkey': 'PublicKey',
|
||||
'short_channel_id': 'ShortChannelId',
|
||||
|
@ -35,6 +36,7 @@ typemap = {
|
|||
'outputdesc': 'OutputDesc',
|
||||
'hash': 'Sha256',
|
||||
'secret': 'Secret',
|
||||
'bip340sig': 'Secret',
|
||||
'integer': 'i64',
|
||||
}
|
||||
|
||||
|
|
|
@ -234,12 +234,6 @@ class CompositeField(Field):
|
|||
logger.warning(f"Unmanaged {fpath}, it doesn't have a type")
|
||||
continue
|
||||
|
||||
elif ftype["type"] == ["string", "null"]:
|
||||
# TODO Remove the `['string', 'null']` match once
|
||||
# `listpeers.peers[].channels[].closer` no longer has this
|
||||
# type
|
||||
ftype["type"] = "string"
|
||||
|
||||
# Peek into the type so we know how to decode it
|
||||
elif ftype["type"] in ["string", ["string", "null"]] and "enum" in ftype:
|
||||
field = EnumField.from_js(ftype, fpath)
|
||||
|
@ -360,6 +354,7 @@ class PrimitiveField(Field):
|
|||
"msat",
|
||||
"msat_or_any",
|
||||
"msat_or_all",
|
||||
"currency",
|
||||
"hex",
|
||||
"short_channel_id",
|
||||
"short_channel_id_dir",
|
||||
|
@ -372,6 +367,7 @@ class PrimitiveField(Field):
|
|||
"utxo", # A string representing the tuple (txid, outnum)
|
||||
"outputdesc", # A dict that maps an address to an amount (bitcoind style)
|
||||
"secret",
|
||||
"bip340sig",
|
||||
"hash",
|
||||
]
|
||||
|
||||
|
@ -435,8 +431,10 @@ class Command:
|
|||
return f"Command[name={self.name}, fields=[{fieldnames}]]"
|
||||
|
||||
|
||||
OfferStringField = PrimitiveField("string", None, None, added=None, deprecated=None)
|
||||
InvoiceLabelField = PrimitiveField("string", None, None, added=None, deprecated=None)
|
||||
DatastoreKeyField = ArrayField(itemtype=PrimitiveField("string", None, None, added=None, deprecated=None), dims=1, path=None, description=None, added=None, deprecated=None)
|
||||
DatastoreUsageKeyField = ArrayField(itemtype=PrimitiveField("string", None, None, added="v23.11", deprecated=None), dims=1, path=None, description=None, added="v23.11", deprecated=None)
|
||||
InvoiceExposeprivatechannelsField = PrimitiveField("boolean", None, None, added=None, deprecated=None)
|
||||
PayExclude = ArrayField(itemtype=PrimitiveField("string", None, None, added=None, deprecated=None), dims=1, path=None, description=None, added=None, deprecated=None)
|
||||
RoutehintListField = PrimitiveField(
|
||||
|
@ -470,6 +468,11 @@ overrides = {
|
|||
'Pay.exclude': PayExclude,
|
||||
'KeySend.routehints': RoutehintListField,
|
||||
'KeySend.extratlvs': TlvStreamField,
|
||||
'CreateInvoice.label': InvoiceLabelField,
|
||||
'DatastoreUsage.key': DatastoreUsageKeyField,
|
||||
'WaitInvoice.label': InvoiceLabelField,
|
||||
'Offer.recurrence_base': OfferStringField,
|
||||
'Offer.amount': OfferStringField
|
||||
}
|
||||
|
||||
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -39,10 +39,9 @@ def load_jsonrpc_method(name):
|
|||
"""Load a method based on the file naming conventions for the JSON-RPC.
|
||||
"""
|
||||
schema = get_schema_bundle()
|
||||
req_file = f"{name.lower()}.request.json"
|
||||
resp_file = f"{name.lower()}.schema.json"
|
||||
request = CompositeField.from_js(schema[req_file], path=name)
|
||||
response = CompositeField.from_js(schema[resp_file], path=name)
|
||||
rpc_name = f"lightning-{name.lower()}.json"
|
||||
request = CompositeField.from_js(schema[rpc_name]['request'], path=name)
|
||||
response = CompositeField.from_js(schema[rpc_name]['response'], path=name)
|
||||
|
||||
# Normalize the method request and response typename so they no
|
||||
# longer conflict.
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -432,15 +432,16 @@ def _extra_validator(is_request: bool):
|
|||
type_checker=type_checker)
|
||||
|
||||
|
||||
def _load_schema(filename, is_request):
|
||||
def _load_schema(filename):
|
||||
"""Load the schema from @filename and create a validator for it"""
|
||||
with open(filename, 'r') as f:
|
||||
return _extra_validator(is_request)(json.load(f))
|
||||
data = json.load(f)
|
||||
return [_extra_validator(True)(data.get('request', {})), _extra_validator(False)(data.get('response', {}))]
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def jsonschemas():
|
||||
"""Load schema files if they exist: returns request/response schemas by pairs"""
|
||||
"""Load schema file if it exist: returns request/response schemas by pairs"""
|
||||
try:
|
||||
schemafiles = os.listdir('doc/schemas')
|
||||
except FileNotFoundError:
|
||||
|
@ -448,20 +449,10 @@ def jsonschemas():
|
|||
|
||||
schemas = {}
|
||||
for fname in schemafiles:
|
||||
if fname.endswith('.schema.json'):
|
||||
base = fname.rpartition('.schema')[0]
|
||||
is_request = False
|
||||
index = 1
|
||||
elif fname.endswith('.request.json'):
|
||||
base = fname.rpartition('.request')[0]
|
||||
is_request = True
|
||||
index = 0
|
||||
else:
|
||||
continue
|
||||
if base not in schemas:
|
||||
schemas[base] = [None, None]
|
||||
schemas[base][index] = _load_schema(os.path.join('doc/schemas', fname),
|
||||
is_request)
|
||||
if fname.startswith('lightning-') and fname.endswith('.json'):
|
||||
base = fname.replace('lightning-', '').replace('.json', '')
|
||||
# Request is 0 and Response is 1
|
||||
schemas[base] = _load_schema(os.path.join('doc/schemas', fname))
|
||||
return schemas
|
||||
|
||||
|
||||
|
|
|
@ -512,6 +512,7 @@ def listsendpays_payments2py(m):
|
|||
"bolt11": m.bolt11, # PrimitiveField in generate_composite
|
||||
"description": m.description, # PrimitiveField in generate_composite
|
||||
"bolt12": m.bolt12, # PrimitiveField in generate_composite
|
||||
"completed_at": m.completed_at, # PrimitiveField in generate_composite
|
||||
"payment_preimage": hexlify(m.payment_preimage), # PrimitiveField in generate_composite
|
||||
"erroronion": hexlify(m.erroronion), # PrimitiveField in generate_composite
|
||||
})
|
||||
|
@ -872,6 +873,7 @@ def listpeerchannels_channels2py(m):
|
|||
return remove_default({
|
||||
"peer_id": hexlify(m.peer_id), # PrimitiveField in generate_composite
|
||||
"peer_connected": m.peer_connected, # PrimitiveField in generate_composite
|
||||
"reestablished": m.reestablished, # PrimitiveField in generate_composite
|
||||
"state": str(m.state), # EnumField in generate_composite
|
||||
"scratch_txid": hexlify(m.scratch_txid), # PrimitiveField in generate_composite
|
||||
"ignore_fee_limits": m.ignore_fee_limits, # PrimitiveField in generate_composite
|
||||
|
|
119
doc/Makefile
119
doc/Makefile
|
@ -4,11 +4,8 @@
|
|||
doc-wrongdir:
|
||||
$(MAKE) -C .. doc-all
|
||||
|
||||
MANPAGES := doc/lightning-cli.1 \
|
||||
doc/lightningd.8 \
|
||||
doc/lightningd-config.5 \
|
||||
doc/lightningd-rpc.7 \
|
||||
doc/lightning-addgossip.7 \
|
||||
GENERATE_MARKDOWN := doc/lightning-addgossip.7 \
|
||||
doc/lightning-addpsbtoutput.7 \
|
||||
doc/lightning-autoclean-once.7 \
|
||||
doc/lightning-autoclean-status.7 \
|
||||
doc/lightning-batching.7 \
|
||||
|
@ -23,20 +20,20 @@ MANPAGES := doc/lightning-cli.1 \
|
|||
doc/lightning-checkmessage.7 \
|
||||
doc/lightning-checkrune.7 \
|
||||
doc/lightning-close.7 \
|
||||
doc/lightning-connect.7 \
|
||||
doc/lightning-commando.7 \
|
||||
doc/lightning-commando-blacklist.7 \
|
||||
doc/lightning-commando.7 \
|
||||
doc/lightning-commando-listrunes.7 \
|
||||
doc/lightning-commando-rune.7 \
|
||||
doc/lightning-createonion.7 \
|
||||
doc/lightning-connect.7 \
|
||||
doc/lightning-createinvoice.7 \
|
||||
doc/lightning-createonion.7 \
|
||||
doc/lightning-createrune.7 \
|
||||
doc/lightning-datastore.7 \
|
||||
doc/lightning-datastoreusage.7 \
|
||||
doc/lightning-decodepay.7 \
|
||||
doc/lightning-decode.7 \
|
||||
doc/lightning-decodepay.7 \
|
||||
doc/lightning-deldatastore.7 \
|
||||
doc/lightning-delexpiredinvoice.7 \
|
||||
doc/lightning-delexpiredinvoice.7 \
|
||||
doc/lightning-delforward.7 \
|
||||
doc/lightning-delinvoice.7 \
|
||||
doc/lightning-delpay.7 \
|
||||
|
@ -47,32 +44,35 @@ MANPAGES := doc/lightning-cli.1 \
|
|||
doc/lightning-emergencyrecover.7 \
|
||||
doc/lightning-feerates.7 \
|
||||
doc/lightning-fetchinvoice.7 \
|
||||
doc/lightning-fundchannel_cancel.7 \
|
||||
doc/lightning-fundchannel_complete.7 \
|
||||
doc/lightning-fundchannel.7 \
|
||||
doc/lightning-fundchannel_start.7 \
|
||||
doc/lightning-fundchannel_complete.7 \
|
||||
doc/lightning-fundchannel_cancel.7 \
|
||||
doc/lightning-funderupdate.7 \
|
||||
doc/lightning-addpsbtoutput.7 \
|
||||
doc/lightning-fundpsbt.7 \
|
||||
doc/lightning-getinfo.7 \
|
||||
doc/lightning-getlog.7 \
|
||||
doc/lightning-getroute.7 \
|
||||
doc/lightning-hsmtool.8 \
|
||||
doc/lightning-help.7 \
|
||||
doc/lightning-invoice.7 \
|
||||
doc/lightning-invoicerequest.7 \
|
||||
doc/lightning-keysend.7 \
|
||||
doc/lightning-listchannels.7 \
|
||||
doc/lightning-listclosedchannels.7 \
|
||||
doc/lightning-listconfigs.7 \
|
||||
doc/lightning-listdatastore.7 \
|
||||
doc/lightning-listforwards.7 \
|
||||
doc/lightning-listfunds.7 \
|
||||
doc/lightning-listhtlcs.7 \
|
||||
doc/lightning-listinvoices.7 \
|
||||
doc/lightning-listinvoicerequests.7 \
|
||||
doc/lightning-listinvoices.7 \
|
||||
doc/lightning-listnodes.7 \
|
||||
doc/lightning-listoffers.7 \
|
||||
doc/lightning-listpays.7 \
|
||||
doc/lightning-listpeers.7 \
|
||||
doc/lightning-listpeerchannels.7 \
|
||||
doc/lightning-showrunes.7 \
|
||||
doc/lightning-listpeers.7 \
|
||||
doc/lightning-listsendpays.7 \
|
||||
doc/lightning-listtransactions.7 \
|
||||
doc/lightning-makesecret.7 \
|
||||
doc/lightning-multifundchannel.7 \
|
||||
doc/lightning-multiwithdraw.7 \
|
||||
|
@ -84,61 +84,79 @@ MANPAGES := doc/lightning-cli.1 \
|
|||
doc/lightning-openchannel_init.7 \
|
||||
doc/lightning-openchannel_signed.7 \
|
||||
doc/lightning-openchannel_update.7 \
|
||||
doc/lightning-pay.7 \
|
||||
doc/lightning-parsefeerate.7 \
|
||||
doc/lightning-pay.7 \
|
||||
doc/lightning-ping.7 \
|
||||
doc/lightning-plugin.7 \
|
||||
doc/lightning-preapproveinvoice.7 \
|
||||
doc/lightning-preapprovekeysend.7 \
|
||||
doc/lightning-recover.7 \
|
||||
doc/lightning-recoverchannel.7 \
|
||||
doc/lightning-recover.7 \
|
||||
doc/lightning-renepay.7 \
|
||||
doc/lightning-renepaystatus.7 \
|
||||
doc/lightning-reserveinputs.7 \
|
||||
doc/lightning-sendcustommsg.7 \
|
||||
doc/lightning-sendinvoice.7 \
|
||||
doc/lightning-sendonion.7 \
|
||||
doc/lightning-sendonionmessage.7 \
|
||||
doc/lightning-sendpay.7 \
|
||||
doc/lightning-sendpsbt.7 \
|
||||
doc/lightning-setchannel.7 \
|
||||
doc/lightning-setconfig.7 \
|
||||
doc/lightning-setpsbtversion.7 \
|
||||
doc/lightning-sendcustommsg.7 \
|
||||
doc/lightning-showrunes.7 \
|
||||
doc/lightning-signinvoice.7 \
|
||||
doc/lightning-signmessage.7 \
|
||||
doc/lightning-signpsbt.7 \
|
||||
doc/lightning-splice_init.7 \
|
||||
doc/lightning-splice_update.7 \
|
||||
doc/lightning-splice_signed.7 \
|
||||
doc/lightning-staticbackup.7 \
|
||||
doc/lightning-txprepare.7 \
|
||||
doc/lightning-splice_update.7 \
|
||||
doc/lightning-staticbackup.7 \
|
||||
doc/lightning-stop.7 \
|
||||
doc/lightning-txdiscard.7 \
|
||||
doc/lightning-txprepare.7 \
|
||||
doc/lightning-txsend.7 \
|
||||
doc/lightning-unreserveinputs.7 \
|
||||
doc/lightning-upgradewallet.7 \
|
||||
doc/lightning-utxopsbt.7 \
|
||||
doc/lightning-wait.7 \
|
||||
doc/lightning-waitinvoice.7 \
|
||||
doc/lightning-waitanyinvoice.7 \
|
||||
doc/lightning-waitblockheight.7 \
|
||||
doc/lightning-waitinvoice.7 \
|
||||
doc/lightning-wait.7 \
|
||||
doc/lightning-waitsendpay.7 \
|
||||
doc/lightning-withdraw.7 \
|
||||
doc/lightning-ping.7 \
|
||||
doc/lightning-stop.7 \
|
||||
doc/lightning-signpsbt.7 \
|
||||
doc/lightning-sendpsbt.7 \
|
||||
doc/lightning-getinfo.7 \
|
||||
doc/lightning-listtransactions.7 \
|
||||
doc/lightning-listnodes.7 \
|
||||
doc/lightning-listconfigs.7 \
|
||||
doc/lightning-help.7 \
|
||||
doc/lightning-getlog.7 \
|
||||
doc/reckless.7
|
||||
doc/lightning-withdraw.7
|
||||
|
||||
ifeq ($(HAVE_SQLITE3),1)
|
||||
MANPAGES += doc/lightning-listsqlschemas.7 \
|
||||
doc/lightning-sql.7
|
||||
GENERATE_MARKDOWN += doc/lightning-listsqlschemas.7 \
|
||||
doc/lightning-sql.7
|
||||
endif
|
||||
|
||||
MANPAGES := $(GENERATE_MARKDOWN) \
|
||||
doc/lightning-cli.1 \
|
||||
doc/lightningd.8 \
|
||||
doc/lightningd-config.5 \
|
||||
doc/lightningd-rpc.7 \
|
||||
doc/lightning-hsmtool.8 \
|
||||
doc/reckless.7
|
||||
|
||||
MARKDOWN_WITH_SCHEMA := $(GENERATE_MARKDOWN:=.md)
|
||||
|
||||
doc/schemas/lightning-sql.json: plugins/sql
|
||||
# Delete the old tables schema and generate the new ones.
|
||||
@if [ "$$(jq '[.tables[] | startswith("The following tables are currently supported:")]' doc/schemas/lightning-sql.json | jq 'any')" = "true" ]; then \
|
||||
jq 'del(.tables[] | select(startswith("The following tables are currently supported:")))' "$@" > "$@.tmp" && mv "$@.tmp" "$@"; \
|
||||
fi
|
||||
@plugins/sql --print-docs | jq --arg sqldata "$$(awk '{printf "%s\n", $$0}')" '.tables += [$$sqldata]' "$@" > "$@.tmp" && mv "$@.tmp" "$@";
|
||||
|
||||
doc-all: $(MANPAGES) doc/index.rst
|
||||
|
||||
SCHEMAS := $(wildcard doc/schemas/*.json)
|
||||
SCHEMAS := $(wildcard doc/schemas/lightning-*.json)
|
||||
|
||||
# Don't try to build lightning-sql.json tables with plugins/sql if we don't have sqlite3
|
||||
ifeq ($(HAVE_SQLITE3),0)
|
||||
SCHEMAS := $(filter-out doc/schemas/lightning-sql.json, $(SCHEMAS))
|
||||
endif
|
||||
|
||||
check-fmt-schemas: $(SCHEMAS:%=check-fmt-schema/%)
|
||||
fmt-schemas: $(SCHEMAS:%=fmt-schema/%)
|
||||
|
||||
|
@ -150,15 +168,12 @@ fmt-schema/%: %
|
|||
|
||||
check-doc: check-config-docs check-manpages check-fmt-schemas
|
||||
|
||||
# Some manpages use a schema, so need that added.
|
||||
MARKDOWN_WITH_SCHEMA := $(shell grep -l GENERATE-FROM-SCHEMA $(MANPAGES:=.md))
|
||||
|
||||
# These are hard to use in $(call) functions.
|
||||
LBRACKET=(
|
||||
RBRACKET=)
|
||||
|
||||
$(MARKDOWN_WITH_SCHEMA): doc/lightning-%.7.md: doc/schemas/%.schema.json tools/fromschema.py
|
||||
@if $(call SHA256STAMP_CHANGED); then $(call VERBOSE, "fromschema $@", tools/fromschema.py --markdownfile=$@ doc/schemas/$*.schema.json > $@.tmp && grep -v SHA256STAMP: $@.tmp > $@ && rm -f $@.tmp && $(call SHA256STAMP,[comment]: # $(LBRACKET),$(RBRACKET))); else touch $@; fi
|
||||
$(MARKDOWN_WITH_SCHEMA): doc/lightning-%.7.md: doc/schemas/lightning-%.json tools/fromschema.py
|
||||
@tools/fromschema.py --markdownfile=$@ $< > $@.tmp && mv $@.tmp $@
|
||||
|
||||
# If we need to build lowdown, make tools/md2man.sh depend on it.
|
||||
# That way it's not used in SHA256STAMP (which only uses direct
|
||||
|
@ -172,19 +187,11 @@ LOWDOWN := lowdown
|
|||
endif
|
||||
|
||||
$(MANPAGES): doc/%: doc/%.md tools/md2man.sh version_gen.h
|
||||
@if $(call SHA256STAMP_CHANGED); then $(call VERBOSE, "md2man $<", VERSION=$(VERSION) tools/md2man.sh $(LOWDOWN) $< > $@ && $(call SHA256STAMP,\\\",)); else touch $@; fi
|
||||
@VERSION=$(VERSION) tools/md2man.sh $(LOWDOWN) $< > $@
|
||||
|
||||
$(MANPAGES): $(FORCE)
|
||||
$(MARKDOWN_WITH_SCHEMA): $(FORCE)
|
||||
|
||||
# Use awk for preamble, then again for post, with the new docs in the middle.
|
||||
# We can't put plugins/sql in deps directly, since they all get sha256!
|
||||
doc/.sqlgen: plugins/sql
|
||||
@plugins/sql --print-docs > $@
|
||||
|
||||
doc/lightning-sql.7.md: doc/.sqlgen $(FORCE)
|
||||
@if $(call SHA256STAMP_CHANGED); then $(call VERBOSE, "sql-print-docs $@", awk "/GENERATE-DOC-START/ { print \$$0; exit } { print \$$0 }" < $@ > $@.tmp && cat doc/.sqlgen >> $@.tmp && (awk "/GENERATE-DOC-END/ { PRINT=1 } { if (PRINT) { print \$$0 } }" | grep -v SHA256STAMP) < $@ >> $@.tmp && mv $@.tmp $@ && $(call SHA256STAMP,[comment]: # $(LBRACKET),$(RBRACKET))); else touch $@; fi
|
||||
|
||||
doc/protocol-%.svg: test/test_protocol
|
||||
test/test_protocol --svg < test/commits/$*.script > $@
|
||||
|
||||
|
@ -225,8 +232,8 @@ check-manpages: all-programs check-config-docs default-targets
|
|||
|
||||
# Makes sure that fields mentioned in schema are in man page, and vice versa.
|
||||
check-config-docs:
|
||||
@for c in `sed -n 's/^ "\(.*\)": {/\1/p' doc/schemas/listconfigs.schema.json | grep -v '^# version$$' | grep -v '^plugins$$' | grep -v '^important-plugins$$'`; do if ! grep -q "^ \*\*$$c\*\*" doc/lightningd-config.5.md; then echo "$$c undocumented!"; exit 1; fi; done
|
||||
@for c in `grep -v '\[plugin ' doc/lightningd-config.5.md | sed -n 's/^ \*\*\([^*]*\)\*\*.*/\1/p' | grep -v '^\(help\|version\|mainnet\|testnet\|signet\|plugin\|important-plugin\|plugin-dir\|clear-plugins\)$$'`; do if ! grep -q '"'"$$c"'"' doc/schemas/listconfigs.schema.json; then echo "$$c documented but not in schema!"; exit 1; fi; done
|
||||
@for c in `sed -n 's/^ "\(.*\)": {/\1/p' doc/schemas/lightning-listconfigs.json | grep -v '^# version$$' | grep -v '^plugins$$' | grep -v '^important-plugins$$'`; do if ! grep -q "^ \*\*$$c\*\*" doc/lightningd-config.5.md; then echo "$$c undocumented!"; exit 1; fi; done
|
||||
@for c in `grep -v '\[plugin ' doc/lightningd-config.5.md | sed -n 's/^ \*\*\([^*]*\)\*\*.*/\1/p' | grep -v '^\(help\|version\|mainnet\|testnet\|signet\|plugin\|important-plugin\|plugin-dir\|clear-plugins\)$$'`; do if ! grep -q '"'"$$c"'"' doc/schemas/lightning-listconfigs.json; then echo "$$c documented but not in schema!"; exit 1; fi; done
|
||||
|
||||
doc-maintainer-clean:
|
||||
$(RM) $(MANPAGES)
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -6,6 +6,18 @@ from argparse import ArgumentParser
|
|||
import json
|
||||
import re
|
||||
|
||||
# To maintain the sequence of the before return value (body) and after return value (footer) sections in the markdown file
|
||||
BODY_KEY_SEQUENCE = ['reliability', 'usage', 'restriction_format', 'example_usage', 'example_json_request', 'notes', 'notifications', 'sharing_runes', 'riskfactor_effect_on_routing', 'recommended_riskfactor_values', 'optimality', 'randomization']
|
||||
FOOTER_KEY_SEQUENCE = ['example_json_response', 'errors', 'example_json_notifications', 'trivia', 'author', 'see_also', 'resources']
|
||||
|
||||
|
||||
def output_title(title, underline='-', num_leading_newlines=1, num_trailing_newlines=2):
|
||||
"""Add a title to the output"""
|
||||
print('\n' * num_leading_newlines + title, end='\n')
|
||||
print(underline * len(title) + '\n' * num_trailing_newlines, end='')
|
||||
global current_line_width
|
||||
current_line_width = 0
|
||||
|
||||
|
||||
def esc_underscores(s):
|
||||
"""Backslash-escape underscores outside of backtick-enclosed spans"""
|
||||
|
@ -25,9 +37,9 @@ def json_value(obj):
|
|||
assert False
|
||||
|
||||
|
||||
def outputs(lines):
|
||||
def outputs(lines, separator=''):
|
||||
"""Add these lines to the final output"""
|
||||
print(''.join(lines), end='')
|
||||
print(esc_underscores(separator.join(lines)), end='')
|
||||
|
||||
|
||||
def output(line):
|
||||
|
@ -35,30 +47,68 @@ def output(line):
|
|||
print(line, end='')
|
||||
|
||||
|
||||
def search_key_in_conditional_array(request, param):
|
||||
"""search param in all conditional subarrays/objects and return the condition and found array/obj"""
|
||||
one_of_many_array = request.get('oneOfMany', [])
|
||||
paired_with_array = request.get('pairedWith', [])
|
||||
|
||||
# Check if the same parameter is in both 'pairedWith' and 'oneOfMany' and throw an error if found
|
||||
common_key = next((element_one for subarray_one in one_of_many_array for element_one in subarray_one for subarray_paired in paired_with_array if element_one in subarray_paired), '')
|
||||
assert common_key == '', f'The same parameter "{common_key}" cannot be in both "pairedWith" and "oneOfMany"'
|
||||
|
||||
# Search for the parameter in 'oneOfMany' array
|
||||
for sub_array_one in one_of_many_array:
|
||||
if param in sub_array_one:
|
||||
return 'oneOfMany', sub_array_one
|
||||
|
||||
# Search for the parameter in 'pairedWith' array
|
||||
for sub_array_paired in paired_with_array:
|
||||
if param in sub_array_paired:
|
||||
return 'pairedWith', sub_array_paired
|
||||
|
||||
# If param doesn't exist in any of the conditional arrays, return empty condition and None
|
||||
return '', None
|
||||
|
||||
|
||||
def output_conditional_params(conditional_sub_array, condition):
|
||||
"""Output request parameters with appropriate separator based on the separator"""
|
||||
# If the request has 'oneOfMany', then print them in one param section with OR (|) sign.
|
||||
# 'oneOfMany' example `plugin`: [*plugin|directory*]
|
||||
# If the request has 'pairedWith', then print them in one param section separated with space.
|
||||
# 'pairedWith' example `delpay`: [*partid* *groupid*]
|
||||
# If the request has 'dependentUpon', then print them in one param section separated with space.
|
||||
# 'dependentUpon' example `listforwards`: [*index* [*start*] [*limit*]]
|
||||
separator = {'oneOfMany': '|', 'pairedWith': '* *', 'dependentUpon': '*] [*'}.get(condition, '')
|
||||
# Join all keys with the separator
|
||||
keysfoundstr = format(esc_underscores(separator.join(conditional_sub_array)))
|
||||
# Print the merged keys
|
||||
output('{}{}'.format(fmt_paramname(keysfoundstr, True, False), '' if condition == 'dependentUpon' else ' '))
|
||||
|
||||
|
||||
def output_type(properties, is_optional):
|
||||
typename = esc_underscores(properties['type'])
|
||||
"""Add types for request and reponse parameters"""
|
||||
typename = 'one of' if 'oneOf' in properties else esc_underscores(properties['type'])
|
||||
if typename == 'array':
|
||||
typename += ' of {}s'.format(esc_underscores(properties['items']['type']))
|
||||
if 'items' in properties and 'type' in properties['items']:
|
||||
typename += ' of {}s'.format(esc_underscores(properties['items']['type']))
|
||||
if is_optional:
|
||||
typename += ", optional"
|
||||
output(" ({})".format(typename))
|
||||
typename += ', optional'
|
||||
output(' ({})'.format(esc_underscores(typename)))
|
||||
|
||||
|
||||
def output_range(properties):
|
||||
if 'maximum' and 'minimum' in properties:
|
||||
output(" ({} to {} inclusive)".format(properties['minimum'],
|
||||
properties['maximum']))
|
||||
output(' ({} to {} inclusive)'.format(properties['minimum'], properties['maximum']))
|
||||
elif 'maximum' in properties:
|
||||
output(" (max {})".format(properties['maximum']))
|
||||
output(' (max {})'.format(properties['maximum']))
|
||||
elif 'minimum' in properties:
|
||||
output(" (min {})".format(properties['minimum']))
|
||||
output(' (min {})'.format(properties['minimum']))
|
||||
|
||||
if 'maxLength' and 'minLength' in properties:
|
||||
if properties['minLength'] == properties['maxLength']:
|
||||
output(' (always {} characters)'.format(properties['minLength']))
|
||||
else:
|
||||
output(' ({} to {} characters)'.format(properties['minLength'],
|
||||
properties['maxLength']))
|
||||
output(' ({} to {} characters)'.format(properties['minLength'], properties['maxLength']))
|
||||
elif 'maxLength' in properties:
|
||||
output(' (up to {} characters)'.format(properties['maxLength']))
|
||||
elif 'minLength' in properties:
|
||||
|
@ -76,11 +126,34 @@ def fmt_propname(propname):
|
|||
return '**{}**'.format(esc_underscores(propname))
|
||||
|
||||
|
||||
def fmt_paramname(paramname, is_optional=True, trailing_space=True):
|
||||
"""Pretty-print format a parameter name"""
|
||||
return '[*{}*]{}'.format(esc_underscores(paramname), ' ' if trailing_space else '') if is_optional else '*{}*{}'.format(esc_underscores(paramname), ' ' if trailing_space else '')
|
||||
|
||||
|
||||
def deprecated_to_deleted(vername):
|
||||
"""We promise a 6 month minumum deprecation period, and versions are every 3 months"""
|
||||
assert vername.startswith('v')
|
||||
base = [int(s) for s in vername[1:].split('.')[0:2]]
|
||||
if base == [0, 12]:
|
||||
base = [22, 8]
|
||||
base[1] += 9
|
||||
if base[1] > 12:
|
||||
base[0] += 1
|
||||
base[1] -= 12
|
||||
# Christian points out versions should sort well lexographically,
|
||||
# so we zero-pad single-digits.
|
||||
return 'v{}.{:0>2}'.format(base[0], base[1])
|
||||
|
||||
|
||||
def output_member(propname, properties, is_optional, indent, print_type=True, prefix=None):
|
||||
"""Generate description line(s) for this member"""
|
||||
# Skip hidden properties
|
||||
if 'hidden' in properties and properties['hidden']:
|
||||
return
|
||||
|
||||
if prefix is None:
|
||||
prefix = '- ' + fmt_propname(propname)
|
||||
prefix = '- ' + fmt_propname(propname) if propname is not None else '-'
|
||||
output(indent + prefix)
|
||||
|
||||
# We make them explicitly note if they don't want a type!
|
||||
|
@ -89,19 +162,25 @@ def output_member(propname, properties, is_optional, indent, print_type=True, pr
|
|||
if not is_untyped and print_type:
|
||||
output_type(properties, is_optional)
|
||||
|
||||
if 'description' in properties:
|
||||
output(": {}".format(esc_underscores(properties['description'])))
|
||||
|
||||
output_range(properties)
|
||||
|
||||
if 'deprecated' in properties:
|
||||
assert isinstance(properties['deprecated'], list)
|
||||
assert len(properties['deprecated']) == 2
|
||||
output(" **deprecated in {}, removed after {}**".format(properties['deprecated'][0], properties['deprecated'][1]))
|
||||
if 'added' in properties:
|
||||
output(" *(added {})*".format(properties['added']))
|
||||
if 'description' in properties:
|
||||
for i in range(0, len(properties['description'])):
|
||||
output('{} {}{}'.format(':' if i == 0 else '', esc_underscores(properties['description'][i]), '' if i + 1 == len(properties['description']) else '\n'))
|
||||
|
||||
if not is_untyped and properties['type'] == 'object':
|
||||
if 'default' in properties:
|
||||
output(' The default is {}.'.format(esc_underscores(properties['default']) if isinstance(properties['default'], str) else properties['default']))
|
||||
|
||||
if 'deprecated' in properties:
|
||||
output(' **deprecated in {}, removed after {}**'.format(properties['deprecated'][0], properties['deprecated'][1] if len(properties['deprecated']) > 1 else deprecated_to_deleted(properties['deprecated'][0])))
|
||||
|
||||
if 'added' in properties:
|
||||
output(' *(added {})*'.format(properties['added']))
|
||||
|
||||
if 'oneOf' in properties and isinstance(properties['oneOf'], list):
|
||||
output(':\n')
|
||||
output_members(properties, indent + ' ')
|
||||
elif not is_untyped and properties['type'] == 'object':
|
||||
output(':\n')
|
||||
output_members(properties, indent + ' ')
|
||||
elif not is_untyped and properties['type'] == 'array':
|
||||
|
@ -113,15 +192,19 @@ def output_member(propname, properties, is_optional, indent, print_type=True, pr
|
|||
|
||||
def output_array(items, indent):
|
||||
"""We've already said it's an array of {type}"""
|
||||
if items['type'] == 'object':
|
||||
if 'oneOf' in items and isinstance(items['oneOf'], list):
|
||||
output_members(items, indent + ' ')
|
||||
elif items['type'] == 'object':
|
||||
output_members(items, indent)
|
||||
elif items['type'] == 'array':
|
||||
output(indent + '- {}:\n'.format(esc_underscores(items['description'])))
|
||||
output_array(items['items'], indent + ' ')
|
||||
output(indent + '-')
|
||||
output_type(items, False)
|
||||
output(': {}\n'.format(esc_underscores('\n'.join(items['description']))) if 'description' in items and len(items['description']) > 0 else '\n')
|
||||
if 'items' in items:
|
||||
output_array(items['items'], indent + ' ')
|
||||
else:
|
||||
output(indent + '- {}'.format(esc_underscores(items['description'])))
|
||||
output_range(items)
|
||||
output('\n')
|
||||
if 'type' in items:
|
||||
output_member(None, items, True, indent)
|
||||
|
||||
|
||||
def has_members(sub):
|
||||
|
@ -138,46 +221,37 @@ def has_members(sub):
|
|||
def output_members(sub, indent=''):
|
||||
"""Generate lines for these properties"""
|
||||
warnings = []
|
||||
if 'properties' in sub:
|
||||
for p in list(sub['properties'].keys()):
|
||||
if len(sub['properties'][p]) == 0 or sub['properties'][p].get('deprecated') is True:
|
||||
del sub['properties'][p]
|
||||
elif p.startswith('warning'):
|
||||
warnings.append(p)
|
||||
|
||||
# Remove deprecated: True and stub properties, collect warnings
|
||||
# (Stubs required to keep additionalProperties: false happy)
|
||||
# First list always-present properties
|
||||
for p in sub['properties']:
|
||||
if p.startswith('warning'):
|
||||
continue
|
||||
if 'required' in sub and p in sub['required']:
|
||||
output_member(p, sub['properties'][p], False, indent)
|
||||
|
||||
# FIXME: It fails for schemas which have only an array type with
|
||||
# no properties, ex:
|
||||
# "abcd": {
|
||||
# "type": "array",
|
||||
# "items": {
|
||||
# "type": "whatever",
|
||||
# "description": "efgh"
|
||||
# }
|
||||
# }
|
||||
# Checkout the schema of `staticbackup`.
|
||||
for p in list(sub['properties'].keys()):
|
||||
if len(sub['properties'][p]) == 0 or sub['properties'][p].get('deprecated') is True:
|
||||
del sub['properties'][p]
|
||||
elif p.startswith('warning'):
|
||||
warnings.append(p)
|
||||
|
||||
# First list always-present properties
|
||||
for p in sub['properties']:
|
||||
if p.startswith('warning'):
|
||||
continue
|
||||
if 'required' in sub and p in sub['required']:
|
||||
output_member(p, sub['properties'][p], False, indent)
|
||||
|
||||
for p in sub['properties']:
|
||||
if p.startswith('warning'):
|
||||
continue
|
||||
if 'required' not in sub or p not in sub['required']:
|
||||
output_member(p, sub['properties'][p], True, indent)
|
||||
for p in sub['properties']:
|
||||
if p.startswith('warning'):
|
||||
continue
|
||||
if 'required' not in sub or p not in sub['required']:
|
||||
output_member(p, sub['properties'][p], True, indent)
|
||||
|
||||
if warnings != []:
|
||||
output(indent + "- the following warnings are possible:\n")
|
||||
output(indent + '- the following warnings are possible:\n')
|
||||
for w in warnings:
|
||||
output_member(w, sub['properties'][w], False, indent + ' ', print_type=False)
|
||||
|
||||
# Not handled.
|
||||
assert 'oneOf' not in sub
|
||||
if 'oneOf' in sub:
|
||||
for oneOfItem in sub['oneOf']:
|
||||
if 'type' in oneOfItem and oneOfItem['type'] == 'array':
|
||||
output_array(oneOfItem, indent)
|
||||
else:
|
||||
output_member(None, oneOfItem, False, indent, False if 'enum' in oneOfItem else True)
|
||||
|
||||
# If we have multiple ifs, we have to wrap them in allOf.
|
||||
if 'allOf' in sub:
|
||||
|
@ -191,43 +265,110 @@ def output_members(sub, indent=''):
|
|||
for ifclause in ifclauses:
|
||||
conditions = []
|
||||
|
||||
# "required" are fields that simply must be present
|
||||
# 'required' are fields that simply must be present
|
||||
for r in ifclause['if'].get('required', []):
|
||||
conditions.append(fmt_propname(r) + ' is present')
|
||||
|
||||
# "properties" are enums of field values
|
||||
# 'properties' are enums of field values
|
||||
for tag, vals in ifclause['if'].get('properties', {}).items():
|
||||
# Don't have a description field here, it's not used.
|
||||
assert 'description' not in vals
|
||||
whichvalues = vals['enum']
|
||||
|
||||
cond = fmt_propname(tag) + " is"
|
||||
cond = fmt_propname(tag) + ' is'
|
||||
if len(whichvalues) == 1:
|
||||
cond += " {}".format(json_value(whichvalues[0]))
|
||||
cond += ' {}'.format(json_value(whichvalues[0]))
|
||||
else:
|
||||
cond += " {} or {}".format(", ".join([json_value(v) for v in whichvalues[:-1]]),
|
||||
cond += ' {} or {}'.format(', '.join([json_value(v) for v in whichvalues[:-1]]),
|
||||
json_value(whichvalues[-1]))
|
||||
conditions.append(cond)
|
||||
|
||||
sentence = indent + "If " + ", and ".join(conditions) + ":\n\n"
|
||||
sentence = indent + 'If ' + ', and '.join(conditions) + ':\n'
|
||||
|
||||
if has_members(ifclause['then']):
|
||||
# Prefix with blank line.
|
||||
outputs(['\n', sentence])
|
||||
|
||||
output_members(ifclause['then'], indent + ' ')
|
||||
|
||||
|
||||
def generate_from_schema(schema):
|
||||
def generate_header(schema):
|
||||
"""Generate lines for rpc title and synopsis with request parameters"""
|
||||
output_title(esc_underscores(''.join(['lightning-', schema['rpc'], ' -- ', schema['title']])), '=', 0, 1)
|
||||
output_title('SYNOPSIS')
|
||||
# Add command level warning if exists
|
||||
if 'warning' in schema:
|
||||
output('**(WARNING: {})**\n\n'.format(esc_underscores(schema['warning'])))
|
||||
# generate the rpc command details with request parameters
|
||||
request = schema['request']
|
||||
properties = request['properties']
|
||||
toplevels = list(request['properties'].keys())
|
||||
output('{} '.format(fmt_propname(schema['rpc'])))
|
||||
i = 0
|
||||
while i < len(toplevels):
|
||||
# Skip hidden properties
|
||||
if 'hidden' in properties[toplevels[i]] and properties[toplevels[i]]['hidden']:
|
||||
i += 1
|
||||
continue
|
||||
# Search for the parameter in 'dependentUpon' array
|
||||
dependent_upon_obj = request['dependentUpon'] if 'dependentUpon' in request else []
|
||||
if toplevels[i] in dependent_upon_obj:
|
||||
# Output parameters with appropriate separator
|
||||
output('{}*{}* '.format('' if 'required' in request and toplevels[i] in request['required'] else '[', esc_underscores(toplevels[i])))
|
||||
output_conditional_params(dependent_upon_obj[toplevels[i]], 'dependentUpon')
|
||||
toplevels = [key for key in toplevels if key not in dependent_upon_obj[toplevels[i]]]
|
||||
output('{}'.format('' if 'required' in request and toplevels[i] in request['required'] else ']'))
|
||||
else:
|
||||
# Search for the parameter in any conditional sub-arrays (oneOfMany, pairedWith)
|
||||
condition, foundinsubarray = search_key_in_conditional_array(request, toplevels[i])
|
||||
# If param found in the conditional sub-array
|
||||
if condition != '' and foundinsubarray is not None:
|
||||
# Output parameters with appropriate separator
|
||||
output_conditional_params(foundinsubarray, condition)
|
||||
# Remove found keys from toplevels array
|
||||
toplevels = [key for key in toplevels if key not in foundinsubarray]
|
||||
# Reset the cursor to the previous index
|
||||
i = i - 1
|
||||
else:
|
||||
# Print the key as it is if it doesn't exist in conditional array
|
||||
output('{}'.format(fmt_paramname(toplevels[i], False if 'required' in request and toplevels[i] in request['required'] else True)))
|
||||
i += 1
|
||||
# lightning-plugin.json is an exception where all parameters cannot be printed deu to their dependency on different subcommands
|
||||
# So, add ... at the end for lightning-plugin schema
|
||||
if schema['rpc'] == 'plugin':
|
||||
output('...')
|
||||
output('\n')
|
||||
|
||||
|
||||
def generate_description(schema):
|
||||
"""Generate rpc description with request parameter descriptions"""
|
||||
request = schema['request']
|
||||
output_title('DESCRIPTION')
|
||||
# Add deprecated and removal information for the command
|
||||
if 'deprecated' in schema:
|
||||
output('Command **deprecated in {}, removed after {}**.\n\n'.format(schema['deprecated'][0], schema['deprecated'][1] if len(schema['deprecated']) > 1 else deprecated_to_deleted(schema['deprecated'][0])))
|
||||
# Version when the command was added
|
||||
if 'added' in schema:
|
||||
output('Command *added* in {}.\n\n'.format(schema['added']))
|
||||
# Command's detailed description
|
||||
outputs(schema['description'], '\n')
|
||||
# Request parameter's detailed description
|
||||
output('{}'.format('\n\n' if len(request['properties']) > 0 else '\n'))
|
||||
output_members(request)
|
||||
|
||||
|
||||
def generate_return_value(schema):
|
||||
"""This is not general, but works for us"""
|
||||
if schema['type'] != 'object':
|
||||
# 'stop' returns a single string!
|
||||
output_member(None, schema, False, '', prefix='On success, returns a single element')
|
||||
return
|
||||
output_title('RETURN VALUE')
|
||||
|
||||
response = schema['response']
|
||||
|
||||
if 'pre_return_value_notes' in response:
|
||||
outputs(response['pre_return_value_notes'], '\n')
|
||||
output('\n')
|
||||
|
||||
toplevels = []
|
||||
warnings = []
|
||||
props = schema['properties']
|
||||
props = response['properties']
|
||||
|
||||
# We handle warnings on top-level objects with a separate section,
|
||||
# so collect them now and remove them
|
||||
|
@ -240,66 +381,113 @@ def generate_from_schema(schema):
|
|||
|
||||
# No properties -> empty object.
|
||||
if toplevels == []:
|
||||
output('On success, an empty object is returned.\n')
|
||||
# Use pre/post_return_value_notes with empty properties when dynamic generation of the return value section is not required.
|
||||
# But to add a custom return value section instead. Example: `commando` commands.
|
||||
if "pre_return_value_notes" not in response and "post_return_value_notes" not in response:
|
||||
output('On success, an empty object is returned.\n')
|
||||
sub = schema
|
||||
elif len(toplevels) == 1 and props[toplevels[0]]['type'] == 'object':
|
||||
output('On success, an object containing {} is returned. It is an object containing:\n\n'.format(fmt_propname(toplevels[0])))
|
||||
output('On success, an object containing {} is returned. It is an object containing:\n\n'.format(fmt_propname(toplevels[0])))
|
||||
# Don't have a description field here, it's not used.
|
||||
assert 'description' not in toplevels[0]
|
||||
sub = props[toplevels[0]]
|
||||
elif len(toplevels) == 1 and props[toplevels[0]]['type'] == 'array' and props[toplevels[0]]['items']['type'] == 'object':
|
||||
output('On success, an object containing {} is returned. It is an array of objects, where each object contains:\n\n'.format(fmt_propname(toplevels[0])))
|
||||
output('On success, an object containing {} is returned. It is an array of objects, where each object contains:\n\n'.format(fmt_propname(toplevels[0])))
|
||||
# Don't have a description field here, it's not used.
|
||||
assert 'description' not in toplevels[0]
|
||||
sub = props[toplevels[0]]['items']
|
||||
else:
|
||||
output('On success, an object is returned, containing:\n\n')
|
||||
sub = schema
|
||||
sub = response
|
||||
|
||||
output_members(sub)
|
||||
|
||||
if warnings:
|
||||
outputs(['\n', 'The following warnings may also be returned:\n\n'])
|
||||
output('\nThe following warnings may also be returned:\n\n')
|
||||
for w, desc in warnings:
|
||||
output("- {}: {}\n".format(fmt_propname(w), desc))
|
||||
output('- {}: {}\n'.format(fmt_propname(w), ''.join(desc)))
|
||||
|
||||
# GH markdown rendering gets upset if there isn't a blank line
|
||||
# between a list and the end comment.
|
||||
if 'post_return_value_notes' in response:
|
||||
if len(props.keys()) > 0:
|
||||
output('\n')
|
||||
outputs(response['post_return_value_notes'], '\n')
|
||||
output('\n')
|
||||
|
||||
|
||||
def generate_body(schema):
|
||||
"""Output sections which should be printed after description and before return value"""
|
||||
# Insert extra line between description and next section with this flag
|
||||
first_matching_key = True
|
||||
# Only add a newline if at least there is one body key found
|
||||
body_key_found = False
|
||||
for key in BODY_KEY_SEQUENCE:
|
||||
if key not in schema:
|
||||
continue
|
||||
body_key_found = True
|
||||
output_title(key.replace('_', ' ').upper(), '-', 1 if first_matching_key else 2)
|
||||
first_matching_key = False
|
||||
if key == 'example_json_request' and len(schema[key]) > 0:
|
||||
output('```json\n')
|
||||
for example in schema.get(key, []):
|
||||
output(json.dumps(example, indent=2).strip() + '\n')
|
||||
output('```')
|
||||
else:
|
||||
outputs(schema[key], '\n')
|
||||
if body_key_found:
|
||||
output('\n')
|
||||
|
||||
|
||||
def generate_footer(schema):
|
||||
"""Output sections which should be printed after return value"""
|
||||
first_matching_key = True
|
||||
for key in FOOTER_KEY_SEQUENCE:
|
||||
if key not in schema:
|
||||
continue
|
||||
output_title(key.replace('_', ' ').upper(), '-', 1 if first_matching_key else 2)
|
||||
first_matching_key = False
|
||||
if key == 'see_also':
|
||||
# Wrap see_also list with comma separated values
|
||||
output(esc_underscores(', '.join(schema[key])))
|
||||
elif key.startswith('example_json_') and len(schema[key]) > 0:
|
||||
# For printing example_json_response and example_json_notifications into code block
|
||||
for i, example in enumerate(schema.get(key, [])):
|
||||
# For printing string elements from example json response; example: `createonion`
|
||||
if isinstance(example, str):
|
||||
output(example)
|
||||
if i + 1 < len(schema[key]):
|
||||
output('\n')
|
||||
else:
|
||||
if i == 0:
|
||||
output('```json\n')
|
||||
output(json.dumps(example, indent=2).strip() + '\n')
|
||||
if i + 1 == len(schema[key]):
|
||||
output('```')
|
||||
else:
|
||||
outputs(schema[key], '\n')
|
||||
output('\n')
|
||||
|
||||
|
||||
def main(schemafile, markdownfile):
|
||||
start_marker = '[comment]: # (GENERATE-FROM-SCHEMA-START)\n'
|
||||
end_marker = '[comment]: # (GENERATE-FROM-SCHEMA-END)\n'
|
||||
with open(schemafile, 'r') as f:
|
||||
schema = json.load(f)
|
||||
# Outputs rpc title and synopsis with request parameters
|
||||
generate_header(schema)
|
||||
# Outputs command description with request parameter descriptions
|
||||
generate_description(schema)
|
||||
# Outputs other remaining sections before return value section
|
||||
generate_body(schema)
|
||||
# Outputs command response with response parameter descriptions
|
||||
generate_return_value(schema)
|
||||
# Outputs other remaining sections after return value section
|
||||
generate_footer(schema)
|
||||
|
||||
if markdownfile is None:
|
||||
with open(schemafile, "r") as f:
|
||||
schema = json.load(f)
|
||||
generate_from_schema(schema)
|
||||
return
|
||||
|
||||
with open(markdownfile, "r") as f:
|
||||
md = f.readlines()
|
||||
|
||||
suppress_output = False
|
||||
for line in md:
|
||||
if line == end_marker:
|
||||
suppress_output = False
|
||||
|
||||
if not suppress_output:
|
||||
print(line, end='')
|
||||
|
||||
if line == start_marker:
|
||||
with open(schemafile, "r") as f:
|
||||
schema = json.load(f)
|
||||
generate_from_schema(schema)
|
||||
suppress_output = True
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
if __name__ == '__main__':
|
||||
parser = ArgumentParser()
|
||||
parser.add_argument('schemafile', help='The schema file to use')
|
||||
parser.add_argument('--markdownfile', help='The markdown file to read')
|
||||
parsed_args = parser.parse_args()
|
||||
|
||||
main(parsed_args.schemafile, parsed_args.markdownfile)
|
||||
|
|
Loading…
Add table
Reference in a new issue