summaryrefslogtreecommitdiffstats
path: root/rust/src/api_helpers.rs
blob: f0f56a6ba5ce87461aef3f22376be587432930da (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
use data_encoding::BASE32_NOPAD;
use database_models::*;
use database_schema::*;
use diesel;
use diesel::prelude::*;
use errors::*;
use uuid::Uuid;

pub fn get_or_create_editgroup(editor_id: Uuid, conn: &PgConnection) -> Result<Uuid> {
    // check for current active
    let ed_row: EditorRow = editor::table.find(editor_id).first(conn)?;
    if let Some(current) = ed_row.active_editgroup_id {
        return Ok(current);
    }

    // need to insert and update
    conn.build_transaction().run(|| {
        let eg_row: EditgroupRow = diesel::insert_into(editgroup::table)
            .values((editgroup::editor_id.eq(ed_row.id),))
            .get_result(conn)?;
        diesel::update(editor::table.find(ed_row.id))
            .set(editor::active_editgroup_id.eq(eg_row.id))
            .execute(conn)?;
        Ok(eg_row.id)
    })
}

pub fn accept_editgroup(editgroup_id: Uuid, conn: &PgConnection) -> Result<ChangelogRow> {
    conn.build_transaction().run(|| {
        // check that we haven't accepted already (in changelog)
        // NB: could leave this to a UNIQUE constraint
        let count: i64 = changelog::table
            .filter(changelog::editgroup_id.eq(editgroup_id))
            .count()
            .get_result(conn)?;
        if count > 0 {
            bail!(
                "editgroup {} has already been accepted",
                editgroup_id.to_string()
            );
        }

        // for each entity type...
        //for entity in (container_edit, creator_edit, file_edit, release_edit, work_edit) {
            /*
            // This would be the clean and efficient way, but see:
            // https://github.com/diesel-rs/diesel/issues/1478
            diesel::update(container_ident::table)
                .inner_join(container_edit::table.on(
                    container_ident::id.eq(container_edit::ident_id)
                ))
                .filter(container_edit::editgroup_id.eq(editgroup_id))
                .values((
                    container_ident::is_live.eq(true),
                    container_ident::rev_id.eq(container_edit::rev_id),
                    container_ident::redirect_id.eq(container_edit::redirect_id),
                ))
                .execute()?;
            */

        // Sketchy... but fast? Only a few queries per accept.
        for entity in &["container", "creator", "file", "work", "release"] {
            diesel::sql_query(format!(
                "
                    UPDATE {entity}_ident
                    SET
                        is_live = true,
                        rev_id = {entity}_edit.rev_id,
                        redirect_id = {entity}_edit.redirect_id
                    FROM {entity}_edit
                    WHERE
                        {entity}_ident.id = {entity}_edit.ident_id
                        AND {entity}_edit.editgroup_id = $1",
                entity = entity
            )).bind::<diesel::sql_types::Uuid, _>(editgroup_id)
                .execute(conn)?;
        }

        // append log/changelog row
        let entry: ChangelogRow = diesel::insert_into(changelog::table)
            .values((changelog::editgroup_id.eq(editgroup_id),))
            .get_result(conn)?;

        // update any editor's active editgroup
        let no_active: Option<Uuid> = None;
        diesel::update(editor::table)
            .filter(editor::active_editgroup_id.eq(editgroup_id))
            .set(editor::active_editgroup_id.eq(no_active))
            .execute(conn)?;
        Ok(entry)
    })
}

/// Convert fatcat IDs (base32 strings) to UUID
pub fn fcid2uuid(fcid: &str) -> Result<Uuid> {
    if fcid.len() != 26 {
        return Err(ErrorKind::InvalidFatcatId(fcid.to_string()).into());
    }
    let mut raw = vec![0; 16];
    BASE32_NOPAD
        .decode_mut(fcid.to_uppercase().as_bytes(), &mut raw)
        .map_err(|_dp| ErrorKind::InvalidFatcatId(fcid.to_string()))?;
    // unwrap() is safe here, because we know raw is always 16 bytes
    Ok(Uuid::from_bytes(&raw).unwrap())
}

/// Convert UUID to fatcat ID string (base32 encoded)
pub fn uuid2fcid(id: &Uuid) -> String {
    let raw = id.as_bytes();
    BASE32_NOPAD.encode(raw).to_lowercase()
}