patx/relay-lang

mongo = Mongo(env.get("RELAY_MONGO_URI", "mongodb://localhost:27017"))
db = mongo.db(env.get("RELAY_MONGO_DATABASE", "twitter_clone"))
users = db.collection("twitter_clone_users")
posts = db.collection("twitter_clone_posts")
follows = db.collection("twitter_clone_follows")
counters = db.collection("twitter_clone_counters")

fn count_items(items)
    total = 0
    for (item in items)
        total += 1
    return total

fn text_or_empty(value)
    if (value == None)
        return ""
    return str(value)

fn number_or_zero(value)
    if (value == None)
        return 0

    try
        return int(str(value))
    except
        return 0

fn api_error(status, message)
    return Response(
        message,
        status=status,
        content_type="text/plain; charset=utf-8"
    )

fn request_input(name)
    form = request["form"]
    if (form != None)
        value = form[name]
        if (value != None)
            return value

    query = request["query"]
    if (query != None)
        value = query[name]
        if (value != None)
            return value

    json_data = request["json"]
    if (json_data != None)
        value = json_data[name]
        if (value != None)
            return value

    return None

fn username_key(username)
    return "user:" + username

fn follow_key(follower_username, following_username)
    return "follow:" + follower_username + ":" + following_username

fn counter_key(name)
    return "counter:" + name

fn post_key(seq)
    return "post:" + str(seq)

fn find_user(username)
    return users.find_one({"_id": username_key(username)})

fn current_user_doc()
    username = session["username"]
    if (username == None)
        return None

    user = find_user(str(username))
    if (user == None)
        session["username"] = None
        return None

    return user

fn active_viewer_username()
    viewer = current_user_doc()
    if (viewer == None)
        return None
    return str(viewer["username"])

fn next_sequence(name)
    doc = counters.find_one_and_update({"_id": counter_key(name)}, {"$inc": {"value": 1}}, upsert=True)
    return int(doc["value"])

fn is_username_char(ch)
    return (
        ch == "_" ||
        ch == "a" || ch == "b" || ch == "c" || ch == "d" || ch == "e" || ch == "f" ||
        ch == "g" || ch == "h" || ch == "i" || ch == "j" || ch == "k" || ch == "l" ||
        ch == "m" || ch == "n" || ch == "o" || ch == "p" || ch == "q" || ch == "r" ||
        ch == "s" || ch == "t" || ch == "u" || ch == "v" || ch == "w" || ch == "x" ||
        ch == "y" || ch == "z" ||
        ch == "0" || ch == "1" || ch == "2" || ch == "3" || ch == "4" ||
        ch == "5" || ch == "6" || ch == "7" || ch == "8" || ch == "9"
    )

fn is_valid_username(username)
    size = count_items(username)
    if (size < 3 || size > 20)
        return False

    for (ch in username)
        if (is_username_char(ch) == False)
            return False

    return True

fn is_valid_password(password)
    size = count_items(password)
    return (size >= 6 && size <= 72)

fn is_valid_bio(bio)
    return count_items(bio) <= 160

fn is_valid_post_body(body)
    size = count_items(body)
    return (size >= 1 && size <= 280)

fn user_brief_from_doc(user_doc)
    return {
        "username": text_or_empty(user_doc["username"]),
        "bio": text_or_empty(user_doc["bio"])
    }

fn public_user_from_doc(user_doc, viewer_username = None)
    username = text_or_empty(user_doc["username"])
    followers_count = follows.count({"following_username": username})
    following_count = follows.count({"follower_username": username})
    posts_count = posts.count({"author_username": username})

    viewer_is_following = False
    if (viewer_username != None && viewer_username != username)
        existing_follow = follows.find_one({"_id": follow_key(viewer_username, username)})
        viewer_is_following = (existing_follow != None)

    return {
        "username": username,
        "bio": text_or_empty(user_doc["bio"]),
        "joined_seq": number_or_zero(user_doc["joined_seq"]),
        "followers_count": followers_count,
        "following_count": following_count,
        "posts_count": posts_count,
        "viewer_is_following": viewer_is_following,
        "is_self": (viewer_username == username)
    }

fn serialize_post_doc(post_doc)
    author_username = text_or_empty(post_doc["author_username"])
    author_doc = find_user(author_username)

    author = {"username": author_username, "bio": ""}
    if (author_doc != None)
        author = user_brief_from_doc(author_doc)

    return {
        "id": text_or_empty(post_doc["_id"]),
        "seq": number_or_zero(post_doc["seq"]),
        "body": text_or_empty(post_doc["body"]),
        "author": author
    }

fn insert_post_desc(sorted_posts, post)
    out = []
    inserted = False

    for (existing in sorted_posts)
        if (inserted == False && post["seq"] > existing["seq"])
            out += [post]
            inserted = True
        out += [existing]

    if (inserted == False)
        out += [post]

    return out

fn sort_posts_desc(posts_list)
    sorted_posts = []
    for (post in posts_list)
        sorted_posts = insert_post_desc(sorted_posts, post)
    return sorted_posts

fn collect_posts(filter)
    raw_posts = posts.find(filter, sort={"seq": -1}, limit=100)
    items = []
    for (raw_post in raw_posts)
        try
            items += [serialize_post_doc(raw_post)]
        except
            continue
    return sort_posts_desc(items)

fn discover_posts()
    return collect_posts({})

fn user_posts(username)
    return collect_posts({"author_username": username})

fn home_feed_posts(username)
    items = user_posts(username)
    follow_docs = follows.find({"follower_username": username})

    for (follow_doc in follow_docs)
        followed_username = text_or_empty(follow_doc["following_username"])
        followed_posts = user_posts(followed_username)
        for (post in followed_posts)
            items += [post]

    return sort_posts_desc(items)