Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Handle maxTTL deserialization in ManifestCollection by handling -1 as None ? #183

Open
servufi opened this issue Sep 16, 2024 · 0 comments

Comments

@servufi
Copy link

servufi commented Sep 16, 2024

Description:
Implementation of the max_expiry field in ManifestCollection struct assumes it will always hold a valid u64. However, -1 may be returned when no expiration (?) is set? This results in deserialization errors when trying to cast -1 to u64.

Problem:

    // Example call:
    bucket.collections().get_all_scopes(GetAllScopesOptions::default()).await;
    // Output:
    DecodingFailure { ctx: {}, source: Custom { kind: InvalidData, error: Error("invalid value: integer `-1`, expected u64", line: 1, column: 189) } }

    // Caused by couchbase/src/api/collections.rs:
    #[derive(Debug, Deserialize)]
    struct ManifestCollection {
        uid: String,
        name: String,
        #[serde(rename = "maxTTL")]
        max_expiry: u64, // <- This
    }

Workaround couchbase/src/api/collections.rs:

#[derive(Debug, Deserialize)]
struct ManifestCollection {
    uid: String,
    name: String,
    #[serde(rename = "maxTTL", deserialize_with = "deserialize_max_ttl")]
    max_expiry: Option<u64>,  // Option to represent no expiry -1 ?
}

// Custom deserialization function for maxTTL
fn deserialize_max_ttl<'de, D>(deserializer: D) -> Result<Option<u64>, D::Error>
where
    D: serde::Deserializer<'de>,
{
    let value: i64 = serde::Deserialize::deserialize(deserializer)?;
    if value == -1 {
        Ok(None)  // -1 represents no expiry ?
    } else if value >= 0 {
        Ok(Some(value as u64))  // Valid u64 values
    } else {
        Err(serde::de::Error::custom("invalid value for max_expiry"))
    }
}

...

impl CollectionManager {
    ...
    pub async fn get_all_scopes(
        &self,
        options: impl Into<Option<GetAllScopesOptions>>,
    ) -> CouchbaseResult<Vec<ScopeSpec>> {
        ...
        for scope in manifest.scopes {
            let mut collections = vec![];
            for col in scope.collections {
                let expiry_duration = col.max_expiry.map_or(Duration::ZERO, |secs| Duration::from_secs(secs));
                collections.push(CollectionSpec::new(
                    col.name,
                    scope.name.clone(),
                    expiry_duration,
                ))
            }
            scopes.push(ScopeSpec::new(scope.name, collections));
        }
        ...
    }
    ...
}

Worked for me but not sure if something else broke 😆

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant