Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

[enhance] facebook: Partial update of facebook API (untidy).

  • Loading branch information...
commit 97325e2bd651ac5cd64aad44764bb8fee6731cb5 1 parent 001e71c
@nrs135 nrs135 authored
View
45 lib/stdlib/apis/facebook/auth/auth.opa
@@ -77,6 +77,12 @@ type FbAuth.token = {
type FbAuth.token_res =
{ token : FbAuth.token } / { error : Facebook.error }
+/**
+ * Parsed result of a app page token request
+ */
+type FbAuth.page_token_res =
+ { page_token : string } / { error : Facebook.error }
+
FbAuth(conf:Facebook.config) = {{
@@ -163,4 +169,43 @@ FbAuth(conf:Facebook.config) = {{
, ("grant_type","client_credentials") ]
access_token_internal(data)
+ /**
+ * Gets an application page access token. These tokens do not expire.
+ *
+ * @param id The [APP_ID] of the app you want the page token for.
+ * @param page_name The name of the app
+ * @param token Your access token, must have [manage_pages] permission
+ *
+ * @return A [FbAuth.page_token_res] with either a page token or
+ * an error in case of failure
+ */
+ application_page_token(id, page_name, token) : FbAuth.page_token_res =
+ match FbLib.fb_get(graph_url, "/{id}/accounts?access_token={token}", []) with
+ | {none} -> {error=Facebook.make_error("application_page_token","Network error")}
+ | {some=r} ->
+ match Json.of_string(r) with
+ | {some={Record=r}} ->
+ match List.assoc("data",r) with
+ | {some={List=l}} ->
+ match List.find((o ->
+ match o with
+ | {Record=r} ->
+ match List.assoc("name",r) with
+ | {some={String=name}} -> name == page_name
+ | _ -> false
+ end
+ | _ -> false
+ end),l) with
+ | {some={Record=r}} ->
+ match List.assoc("access_token",r) with
+ | {some={String=page_token}} -> ~{page_token} : FbAuth.page_token_res
+ | _ -> {error=Facebook.make_error("application_page_token","No access token in response")}
+ end
+ | _ -> {error=Facebook.make_error("application_page_token","No matching page name")}
+ end
+ | _ -> {error=Facebook.make_error("application_page_token","No data")}
+ end
+ | _ -> {error=Facebook.make_error("application_page_token","Json parse error")}
+ end
+
}}
View
184 lib/stdlib/apis/facebook/facebook.opa
@@ -231,6 +231,69 @@ type Facebook.feed = {
actions : list(Facebook.feed_link)
}
+/** A Facebook post for [FbGraph.Post.post]. */
+type Facebook.post = {
+ message : string
+ link : string
+ picture : string
+ name : string
+ caption : string
+ description : string
+ actions : list(Facebook.feed_link)
+ privacy : string
+}
+
+type Facebook.property =
+ { id } /** The application ID string */
+ / { name } /** The title of the application string */
+ / { description } /** The description of the application written by the 3rd party developers string */
+ / { category } /** The category of the application string */
+ / { company } /** The company the application belongs to string */
+ / { icon_url } /** The URL of the application's icon string */
+ / { subcategory } /** The subcategory of the application string */
+ / { link } /** A link to the Application's profile page string */
+ / { logo_url } /** The URL of the application's logo string */
+ / { daily_active_users } /** The number of daily active users the application has string */
+ / { weekly_active_users } /** The number of weekly active users the application has string */
+ / { monthly_active_users } /** The number of monthly active users the application has string */
+ / { migrations } /** Migrations settings for app profile */
+ / { namespace } /** The namespace for the app */
+ / { restrictions } /** Demographic restrictions set for this app (Object with one or more of the following fields: type, location, age, and age_distr) */
+ / { app_domains } /** Domains and subdomains this app can use (array) */
+ / { auth_dialog_data_help_url } /** The URL of a special landing page that helps users of an app begin publishing Open Graph activity (string) */
+ / { auth_dialog_description } /** The description of an app that appears in the Auth Dialog (string) */
+ / { auth_dialog_headline } /** One line description of an app that appears in the Auth Dialog (string) */
+ / { auth_dialog_perms_explanation } /** The text to explain why an app needs additional permissions that appears in the Auth Dialog (string) */
+ / { auth_referral_user_perms } /** Basic user permissions that a user must grant when Authenticated Referrals are enabled (array) */
+ / { auth_referral_friend_perms } /** Basic friends permissions that a user must grant when Authenticated Referrals are enabled (array) */
+ / { auth_referral_default_activity_privacy } /** The default privacy setting selected for Open Graph activities in the Auth Dialog (string which is one of: SELF, EVERYONE, ALL_FRIENDS or NONE) */
+ / { auth_referral_enabled } /** Indicates whether Authenticated Referrals are enabled (bool) */
+ / { auth_referral_extended_perms } /** Extended permissions that a user can choose to grant when Authenticated Referrals are enabled (array) */
+ / { auth_referral_response_type } /** The format that an app receives the Auth token from the Auth Dialog in (string which is one of: code or token) */
+ / { canvas_fluid_height } /** Indicates whether app uses fluid or settable height values for Canvas (bool) */
+ / { canvas_fluid_width } /** Indicates whether app uses fluid or fixed width values for Canvas (bool) */
+ / { canvas_url } /** The non-secure URL from which Canvas app content is loaded (string) */
+ / { contact_email } /** Email address listed for users to contact developers (string) */
+ / { created_time } /** Unix timestamp that indicates when the app was created int */
+ / { creator_uid } /** User ID of the creator of this app int */
+ / { deauth_callback_url } /** URL that is pinged whenever a user removes the app (string) */
+ / { iphone_app_store_id } /** ID of the app in the iPhone App Store string */
+ / { hosting_url } /** Webspace created with one of our hosting partners for this app string */
+ / { mobile_web_url } /** URL to which Mobile users will be directed when using the app (string) */
+ / { page_tab_default_name } /** The title of the app when used in a Page Tab (string) */
+ / { page_tab_url } /** The non-secure URL from which Page Tab app content is loaded (string) */
+ / { privacy_policy_url } /** The URL that links to a Privacy Policy for the app (string) */
+ / { secure_canvas_url } /** The secure URL from which Canvas app content is loaded (string) */
+ / { secure_page_tab_url } /** The secure URL from which Page Tab app content is loaded (string) */
+ / { server_ip_whitelist } /** App requests must originate from this comma-separated list of IP addresses (string) */
+ / { social_discovery } /** Indicates whether app usage stories show up in the Ticker or News Feed (bool) */
+ / { terms_of_service_url } /** URL to Terms of Service which is linked to in Auth Dialog (string) */
+ / { user_support_email } /** Main contact email for this app (string) */
+ / { user_support_url } /** URL of support for users of an app shown in Canvas footer (string) */
+ / { website_url } /** URL of a website that integrates with this app (string) */
+
+type Facebook.properties = list(Facebook.property)
+
Facebook = {{
/* Facebook static */
@@ -444,6 +507,127 @@ Facebook = {{
| "bookmarked" -> {some={bookmarked}}
| _ -> {none}
+ string_of_property(property:Facebook.property) : string =
+ match property with
+ | { id } -> "id"
+ | { name } -> "name"
+ | { description } -> "description"
+ | { category } -> "category"
+ | { company } -> "company"
+ | { icon_url } -> "icon_url"
+ | { subcategory } -> "subcategory"
+ | { link } -> "link"
+ | { logo_url } -> "logo_url"
+ | { daily_active_users } -> "daily_active_users"
+ | { weekly_active_users } -> "weekly_active_users"
+ | { monthly_active_users } -> "monthly_active_users"
+ | { migrations } -> "migrations"
+ | { namespace } -> "namespace"
+ | { restrictions } -> "restrictions"
+ | { app_domains } -> "app_domains"
+ | { auth_dialog_data_help_url } -> "auth_dialog_data_help_url"
+ | { auth_dialog_description } -> "auth_dialog_description"
+ | { auth_dialog_headline } -> "auth_dialog_headline"
+ | { auth_dialog_perms_explanation } -> "auth_dialog_perms_explanation"
+ | { auth_referral_user_perms } -> "auth_referral_user_perms"
+ | { auth_referral_friend_perms } -> "auth_referral_friend_perms"
+ | { auth_referral_default_activity_privacy } -> "auth_referral_default_activity_privacy"
+ | { auth_referral_enabled } -> "auth_referral_enabled"
+ | { auth_referral_extended_perms } -> "auth_referral_extended_perms"
+ | { auth_referral_response_type } -> "auth_referral_response_type"
+ | { canvas_fluid_height } -> "canvas_fluid_height"
+ | { canvas_fluid_width } -> "canvas_fluid_width"
+ | { canvas_url } -> "canvas_url"
+ | { contact_email } -> "contact_email"
+ | { created_time } -> "created_time"
+ | { creator_uid } -> "creator_uid"
+ | { deauth_callback_url } -> "deauth_callback_url"
+ | { iphone_app_store_id } -> "iphone_app_store_id"
+ | { hosting_url } -> "hosting_url"
+ | { mobile_web_url } -> "mobile_web_url"
+ | { page_tab_default_name } -> "page_tab_default_name"
+ | { page_tab_url } -> "page_tab_url"
+ | { privacy_policy_url } -> "privacy_policy_url"
+ | { secure_canvas_url } -> "secure_canvas_url"
+ | { secure_page_tab_url } -> "secure_page_tab_url"
+ | { server_ip_whitelist } -> "server_ip_whitelist"
+ | { social_discovery } -> "social_discovery"
+ | { terms_of_service_url } -> "terms_of_service_url"
+ | { user_support_email } -> "user_support_email"
+ | { user_support_url } -> "user_support_url"
+ | { website_url } -> "website_url"
+
+ property_of_string(s:string) : option(Facebook.property) =
+ match s with
+ | "id" -> {some={ id }}
+ | "name" -> {some={ name }}
+ | "description" -> {some={ description }}
+ | "category" -> {some={ category }}
+ | "company" -> {some={ company }}
+ | "icon_url" -> {some={ icon_url }}
+ | "subcategory" -> {some={ subcategory }}
+ | "link" -> {some={ link }}
+ | "logo_url" -> {some={ logo_url }}
+ | "daily_active_users" -> {some={ daily_active_users }}
+ | "weekly_active_users" -> {some={ weekly_active_users }}
+ | "monthly_active_users" -> {some={ monthly_active_users }}
+ | "migrations" -> {some={ migrations }}
+ | "namespace" -> {some={ namespace }}
+ | "restrictions" -> {some={ restrictions }}
+ | "app_domains" -> {some={ app_domains }}
+ | "auth_dialog_data_help_url" -> {some={ auth_dialog_data_help_url }}
+ | "auth_dialog_description" -> {some={ auth_dialog_description }}
+ | "auth_dialog_headline" -> {some={ auth_dialog_headline }}
+ | "auth_dialog_perms_explanation" -> {some={ auth_dialog_perms_explanation }}
+ | "auth_referral_user_perms" -> {some={ auth_referral_user_perms }}
+ | "auth_referral_friend_perms" -> {some={ auth_referral_friend_perms }}
+ | "auth_referral_default_activity_privacy" -> {some={ auth_referral_default_activity_privacy }}
+ | "auth_referral_enabled" -> {some={ auth_referral_enabled }}
+ | "auth_referral_extended_perms" -> {some={ auth_referral_extended_perms }}
+ | "auth_referral_response_type" -> {some={ auth_referral_response_type }}
+ | "canvas_fluid_height" -> {some={ canvas_fluid_height }}
+ | "canvas_fluid_width" -> {some={ canvas_fluid_width }}
+ | "canvas_url" -> {some={ canvas_url }}
+ | "contact_email" -> {some={ contact_email }}
+ | "created_time" -> {some={ created_time }}
+ | "creator_uid" -> {some={ creator_uid }}
+ | "deauth_callback_url" -> {some={ deauth_callback_url }}
+ | "iphone_app_store_id" -> {some={ iphone_app_store_id }}
+ | "hosting_url" -> {some={ hosting_url }}
+ | "mobile_web_url" -> {some={ mobile_web_url }}
+ | "page_tab_default_name" -> {some={ page_tab_default_name }}
+ | "page_tab_url" -> {some={ page_tab_url }}
+ | "privacy_policy_url" -> {some={ privacy_policy_url }}
+ | "secure_canvas_url" -> {some={ secure_canvas_url }}
+ | "secure_page_tab_url" -> {some={ secure_page_tab_url }}
+ | "server_ip_whitelist" -> {some={ server_ip_whitelist }}
+ | "social_discovery" -> {some={ social_discovery }}
+ | "terms_of_service_url" -> {some={ terms_of_service_url }}
+ | "user_support_email" -> {some={ user_support_email }}
+ | "user_support_url" -> {some={ user_support_url }}
+ | "website_url" -> {some={ website_url }}
+ | _ -> {none}
+
+ is_editable_property(property:Facebook.property) : bool =
+ match property with
+ | { id } -> false
+ | { name } -> false
+ | { description } -> false
+ | { category } -> false
+ | { company } -> false
+ | { icon_url } -> false
+ | { subcategory } -> false
+ | { link } -> false
+ | { logo_url } -> false
+ | { daily_active_users } -> false
+ | { weekly_active_users } -> false
+ | { monthly_active_users } -> false
+ | { created_time } -> false
+ | { creator_uid } -> false
+ | { iphone_app_store_id } -> false
+ | { hosting_url } -> false
+ | _ -> true
+
empty_feed = {
from = ""
to = ""
View
419 lib/stdlib/apis/facebook/graph/graph.opa
@@ -96,6 +96,13 @@ type FbGraph.Read.object =
/ { error : Facebook.error } /** Request failed */
/**
+ * Result of an raw request, just the response content.
+ */
+type FbGraph.Read.raw =
+ { content : string location : string content_type : string}
+ / { error : Facebook.error }
+
+/**
* Result of a multiple object request
*/
type FbGraph.Read.objects =
@@ -205,6 +212,14 @@ type FbGraph.event = {
}
/**
+ * Type of a Facebook achievement(instance)
+ */
+type FbGraph.achievement = {
+ achievement : string
+ message : string
+}
+
+/**
* Type of a Facebook album
*/
type FbGraph.album = {
@@ -215,6 +230,36 @@ type FbGraph.album = {
}
/**
+ * Type of a Facebook photo
+ */
+type FbGraph.photo = {
+ filename : string
+ content_type : string
+ source : string
+ message : string
+}
+
+/**
+ * Type of a Facebook video
+ */
+type FbGraph.video = {
+ filename : string
+ content_type : string
+ source : string
+ title : string
+ description : string
+}
+
+/**
+ * Type of a Facebook picture
+ */
+type FbGraph.picture = {
+ filename : string
+ content_type : string
+ source : string
+}
+
+/**
* Type of a Facebook link
* Default value is [FbGraph.Post.default_link]
* - [to] ID or username of the profile that this story will be published
@@ -287,11 +332,28 @@ type FbGraph.Insight.insights_res =
{ insights : FbGraph.Insight.insights }
/ { error : Facebook.error }
+type FbGraph.property_value0('a) =
+ { string : string }
+ / { int : int }
+ / { bool : bool }
+ / { object : list((string,'a)) }
+ / { array : list('a) }
+type FbGraph.property_value = FbGraph.property_value0(FbGraph.property_value)
+
+type FbGraph.property = (string,FbGraph.property_value)
+
+type FbGraph.properties = list(FbGraph.property)
+
+type FbGraph.object_type = {user} / {permissions} / {page}
+
+type FbGraph.order_status = {placed} / {settled} / {refunded} / {disputed} / {cancelled}
+
FbGraph = {{
/* Static */
@private graph_url = "https://graph.facebook.com"
+ @private video_url = "https://graph-video.facebook.com"
/* Generic private functions */
@@ -375,9 +437,8 @@ FbGraph = {{
check_for_error(r, on_ok, (x -> { error = x }))
| _ -> { error = Facebook.data_error }
- @private @publish generic_action(path, data, token) : FbGraph.Post.result =
- data = List.add(("access_token", token), data)
- match FbLib.fb_post(graph_url, path, data) with
+ generic_return(res) : FbGraph.Post.result =
+ match res with
| {none} -> { error = Facebook.network_error }
| {some=r} ->
match Json.of_string(r) with
@@ -390,9 +451,37 @@ FbGraph = {{
| {some={Bool=r}} ->
if r then { success = "" }
else { error = Facebook.data_error }
+ | {some={Int=number}} -> {success="{number}"}
| _ -> { error = Facebook.data_error }
end
+ @private @publish generic_action(path, data, token) : FbGraph.Post.result =
+ data = List.add(("access_token", token), data)
+ generic_return(FbLib.fb_post(graph_url, path, data))
+
+ @private @publish generic_action_multi(path, forms, token) : FbGraph.Post.result =
+ forms = List.add({form=("access_token", token)}, forms)
+ generic_return(FbLib.fb_post_multi(graph_url, path, forms))
+
+ @private @publish generic_action_multi_base(base, path, forms, token) : FbGraph.Post.result =
+ forms = List.add({form=("access_token", token)}, forms)
+ generic_return(FbLib.fb_post_multi(base, path, forms))
+
+ @private @publish generic_delete(path, data, token) : FbGraph.Post.result =
+ data = List.add(("access_token", token), data)
+ generic_return(FbLib.fb_delete(graph_url, path, data))
+
+ string_of_object_type(ot:FbGraph.object_type) =
+ match ot with
+ | {user} -> "user"
+ | {permissions} -> "permissions"
+ | {page} -> "page"
+
+ multiuser(id, users, pathname, fieldname) =
+ match users with
+ | [user_id] -> ("/{id}/{pathname}/{user_id}",[])
+ | _ -> ("/{id}/{pathname}",[(fieldname,String.concat(",",users))])
+
/* Submodules */
Delete = {{
@@ -401,13 +490,65 @@ FbGraph = {{
* Deletes object [id]
*/
object(id, token) =
- generic_action("/{id}", [("method","delete")], token)
+ generic_delete("/{id}", [], token)
+
+ /**
+ * Delete achievement(instance) object [id]
+ */
+ achievement(id, token) =
+ generic_delete("/{id}/achievements", [], token)
/**
- * Unlike object [id]
+ * Unlike object [id] (works with various types of id)
*/
unlike(id, token) =
- generic_action("/{id}/likes", [("method","delete")], token)
+ generic_delete("/{id}/likes", [], token)
+
+ /**
+ * Unban a user from an application. The token must be an application access token.
+ */
+ unban(user_id, token) =
+ generic_delete("/{user_id}", [], token)
+
+ /** Remove currency association from app. */
+ payment_currency(app_id, currency_url, token) =
+ generic_delete("/{app_id}", [("currency_url",currency_url)], token)
+
+ /** Remove user role from app. */
+ role(app_id, user, token) =
+ generic_delete("/{app_id}/roles", [("user",user)], token)
+
+ /** Delete subscriptions. */
+ subscriptions(app_id, object:option(FbGraph.object_type), token) =
+ data = FbLib.add_if_filled_opt("object", string_of_object_type, object, [])
+ generic_delete("/{app_id}/subscriptions", data, token)
+
+ /** Delete translations.
+ * The native_hashes actually come from the "Translations" app.
+ */
+ translations(app_id, native_hashes:list((string,string)), token) =
+ data = [("native_hashes",List.to_string_using("[","]",",",List.map(((s,d) -> "\{\"{s}\":\"{d}\"}"),native_hashes)))]
+ generic_delete("/{app_id}/translations", data, token)
+
+ /** Delete all scores. */
+ scores(app_id, token) = generic_delete("/{app_id}/scores", [], token)
+
+ /** Delete a comment. */
+ comment(comment_id, token) = generic_delete("/{comment_id}", [], token)
+
+ /** Uninvite a user. */
+ invited(event_id, user_id, token) = generic_delete("/{event_id}/invited/{user_id}", [], token)
+
+ /** Delete picture. */
+ picture(id, token) = generic_delete("/{id}/picture", [], token)
+
+ /** Delete friendlist. */
+ friendlist = object
+
+ /** Delete members. */
+ members(id, users, token) =
+ (path, data) = multiuser(id, users, "members", "members")
+ generic_delete(path, data, token)
}}
@@ -505,6 +646,205 @@ FbGraph = {{
Post = {{
+ string_of_property_value(pv:FbGraph.property_value) =
+ match pv with
+ | {~string} -> "\"{string}\""
+ | {~int} -> "{int}"
+ | {~bool} -> "{bool}"
+ | {~object} -> List.to_string_using("\{","}",",",List.map(((n,v) -> "\"{n}\":{string_of_property_value(v)}"),object))
+ | {~array} -> List.to_string_using("[","]",",",List.map(string_of_property_value,array))
+
+ options_of_properties(properties:FbGraph.properties) =
+ List.map(((n,pv) -> (n,string_of_property_value(pv))),properties)
+
+ generic_properties(id, properties, token) =
+ generic_action("/{id}", options_of_properties(properties), token)
+
+ /**
+ * Create achievement(instance) for [user_id]
+ */
+ achievement(user_id, achievement:FbGraph.achievement, token) =
+ data = FbGraph_to_string.achievement_to_data(achievement)
+ generic_action("/{user_id}/achievements", data, token)
+
+ /**
+ * Create migration for [app_id], [name]:[value]
+ */
+ migration(app_id, name, value:int, token) =
+ properties = [("migrations",{object=[(name,{int=(if value == 0 then 0 else 1)})]})]
+ generic_properties(app_id, properties, token)
+
+ /**
+ * Create restriction for [app_id], [name]:"[value]"
+ */
+ restriction(app_id, name, value:string, token) =
+ properties = [("restrictions",{object=[(name,{string=value})]})]
+ generic_properties(app_id, properties, token)
+
+ /**
+ * Modify properties for [app_id]
+ */
+ properties(app_id, properties, token) =
+ generic_properties(app_id, properties, token)
+
+ /**
+ * Create a test user for [app_id].
+ * NOTE: uses the application access token *NOT* the application Page access token.
+ */
+ test_user(app_id, installed:option(bool), permissions:list(Facebook.permission), name:string, token) =
+ data =
+ FbLib.add_if_filled("installed", Option.default("",Option.map(Bool.to_string,installed)), [])
+ |> FbLib.add_if_filled("permissions", String.concat(",",List.map(Facebook.permission_to_string,permissions)), _)
+ |> FbLib.add_if_filled("name", name, _)
+ generic_action("/{app_id}/accounts/test-users", data, token)
+
+ ban(app_id, users, token) =
+ data = [("uid",String.concat(",",users))]
+ generic_action("/{app_id}/banned", data, token)
+
+ simple_link(app_id, link, message, token) =
+ data =
+ [("link", link)]
+ |> FbLib.add_if_filled("message", message, _)
+ generic_action("/{app_id}/feed", data, token)
+
+ payment_currency(app_id, currency_url, token) =
+ data = [("currency_url", currency_url)]
+ generic_action("/{app_id}/payment_currencies", data, token)
+
+ /**
+ * Create a post on an application's profile page.
+ * Token needs [publish_stream] permissions.
+ * Works on various ids.
+ */
+ post(id, p:Facebook.post, token) =
+ data =
+ [("message",p.message),("link", p.link)]
+ |> FbLib.add_if_filled("picture", p.picture, _)
+ |> FbLib.add_if_filled("name", p.name, _)
+ |> FbLib.add_if_filled("caption", p.caption, _)
+ |> FbLib.add_if_filled("description", p.description, _)
+ |> FbLib.add_array_if_filled("actions", FbLib.actions_to_json, p.actions, _)
+ |> FbLib.add_if_filled("privacy", p.privacy, _)
+ generic_action("/{id}/feed", data, token)
+
+ /** Add user role for app. */
+ role(app_id, user, role, token) =
+ data = [("user",user),("role", role)]
+ generic_action("/{app_id}/roles", data, token)
+
+ /** Post a status message on an object. Various objects supported. */
+ status(id, message, token) =
+ data = [("message",message)]
+ generic_action("/{id}/feed", data, token)
+
+ /**
+ * Set up a subscription.
+ * Needs application page access token.
+ */
+ subscriptions(app_id, ot:FbGraph.object_type, fields:Facebook.properties, callback_url:string, verify_token:string, token) =
+ data =
+ [("object",string_of_object_type(ot))]
+ |> FbLib.add_json("fields", FbLib.fb_properties_to_json, fields, _)
+ |> List.add(("callback_url",callback_url),_)
+ |> FbLib.add_if_filled("verify_token", verify_token, _)
+ generic_action("/{app_id}/subscriptions", data, token)
+
+ /**
+ * Upload application strings for translation.
+ * Needs application page access token.
+ */
+ native_strings_to_json(native_strings:list({text:string; description:string})) : RPC.Json.json =
+ aux(ns) : RPC.Json.json =
+ {Record=[("text", {String=ns.text}:RPC.Json.json),
+ ("description", {String=ns.description}:RPC.Json.json)]}
+ {List=List.map(aux, native_strings)}
+
+ translations(app_id, native_strings:list({text:string; description:string}), token) =
+ data = FbLib.add_json("native_strings",native_strings_to_json,native_strings,[])
+ generic_action("/{app_id}/translations", data, token)
+
+ /**
+ * Requires multipart/form-data forms.
+ * NOTE: should also work with USER_ID and EVENT_ID (and maybe others).
+ * NOTE: this currently doesn't work because of facebook internal errors...
+ */
+ photos(album_id, photo, token) =
+ forms = FbGraph_to_string.photo_to_form(photo)
+ generic_action_multi("/{album_id}/photos", forms, token)
+
+ /**
+ * Publish a video to an Application.
+ * Also works with event_id.
+ * Requires multipart/form-data forms.
+ * NOTE: this currently doesn't work because of facebook internal errors...
+ */
+ videos(page_id, v:FbGraph.video, token) =
+ forms =
+ [{file=("source", v.filename, v.content_type, v.source)}]
+ |> FbLib.add_form_if_filled("title", v.title, _)
+ |> FbLib.add_form_if_filled("description", v.description, _)
+ generic_action_multi_base(video_url, "/{page_id}/videos", forms, token)
+
+ /**
+ * Requires multipart/form-data forms.
+ * NOTE: this currently doesn't work because of facebook internal errors...
+ */
+ picture(id, p:FbGraph.picture, token) =
+ forms = [{file=("source", p.filename, p.content_type, p.source)}]
+ generic_action_multi("/{id}/picture", forms, token)
+
+ users_generic(id, users:list(string), pathname, fieldname, token) =
+ if users == []
+ then {error=Facebook.make_error("FbGraph.Post.{pathname}","No users")}
+ else
+ (path,data) = multiuser(id, users, pathname, fieldname)
+ generic_action(path, data, token)
+
+ /**
+ * Invite users to an event.
+ * Needs [create_event] permission.
+ */
+ invited(event_id, invited:list(string), token) = users_generic(event_id, invited, "invited", "users", token)
+
+ /**
+ * Create a FriendList for a user.
+ * Maximum name length is 25 chars.
+ * Needs manage_friendlists permission.
+ */
+ friendlist(profile_id, name, token) =
+ if String.length(name) > 25
+ then {error=Facebook.make_error("friendlist","Name longer than 25 characters")}
+ else
+ data = [("name",name)]
+ generic_action("/{profile_id}/friendlists", data, token)
+
+ /**
+ * Invite users to an event.
+ * Needs [create_event] permission.
+ */
+ members(friendlist_id, members:list(string), token) = users_generic(friendlist_id, members, "members", "members", token)
+
+ string_of_order_status(os:FbGraph.order_status) =
+ match os with
+ | {placed} -> "placed"
+ | {settled} -> "settled"
+ | {refunded} -> "refunded"
+ | {disputed} -> "disputed"
+ | {cancelled} -> "cancelled"
+
+ /**
+ * Update an order.
+ * Docs don't say which kind of token/permission is needed.
+ * The params value is: "optional JSON-encoded dictionary {'comment' => }", however that is encoded.
+ */
+ order(id, status:FbGraph.order_status, message:string, params:string, token) =
+ data =
+ [("status",string_of_order_status(status))]
+ |> List.add(("message",message),_)
+ |> FbLib.add_if_filled("params", params, _)
+ generic_action("/{id}", data, token)
+
/**
* Post a new element in user's feed
* You can control the target of the feed with [feed.to].
@@ -522,7 +862,7 @@ FbGraph = {{
generic_action("/{object_id}/comments", data, token)
/**
- * Like [object_id] element on behalf of logger user
+ * Like [object_id] element on behalf of logger user (works with various types of id)
*/
like(object_id, token) =
generic_action("/{object_id}/likes", [], token)
@@ -614,17 +954,13 @@ FbGraph = {{
}
/**
- * Post a field
+ * Post an album
*/
album(profile_id, album, token) =
data = FbGraph_to_string.album_to_data(album)
generic_action("/{profile_id}/albums", data, token)
- /* Requires multipart/form-data forms
- photos(album_id, photo)
- */
-
- /* Quite recent feature and extremely poorly documented, so TODO later
+ /* NOTE: Publishing a Checkin object is deprecated in favor of creating a Post with a location attached.
checkins(profile_id, checkin)
*/
@@ -704,6 +1040,20 @@ FbGraph = {{
| _ -> { error = Facebook.data_error }
end
+ redirect(id, options) =
+ data = prepare_object_option(options)
+ FbLib.fb_get_redirect(graph_url, "/{id}", data)
+
+ /**
+ * Return the raw content of a request.
+ * Eg. profile pictures return jpeg data.
+ */
+ raw(id, options) : FbGraph.Read.raw =
+ data = prepare_object_option(options)
+ match FbLib.fb_get_ct(graph_url, "/{id}", data) with
+ | {none} -> { error = Facebook.network_error }
+ | {some=(content_type,location,content)} -> ~{ content; location; content_type }
+
/**
* Get information about objects with given ids, Results depends of options
* If no token is provided, only public information is available. Else,
@@ -777,6 +1127,41 @@ FbGraph = {{
data = [("type",FbGraph_to_string.pic_size_to_string(size))]
FbLib.generic_build_path("{graph_url}/{id}/picture", data)
+ /**
+ * Performs a read on [app_id] with added fields="migrations".
+ */
+ migrations(app_id, options) = object(app_id, {options with fields=List.add("migrations",options.fields)})
+
+ /**
+ * Performs a read on [app_id] with added fields="restrictions".
+ */
+ restrictions(app_id, options) = object(app_id, {options with fields=List.add("restrictions",options.fields)})
+
+ /**
+ * Returns a list of properties for an app.
+ */
+ properties(app_id, options, properties:list(Facebook.property)) =
+ props = String.concat(",",List.map(Facebook.string_of_property,properties))
+ object(app_id, {options with fields=List.add(props,options.fields)})
+
+ banned(app_id, options) = object("{app_id}/banned", options)
+ is_banned(app_id, user_id, options) = object("{app_id}/banned/{user_id}", options)
+ payment_currency(app_id, options) = object("{app_id}/payment_currencies", options)
+ roles(app_id, options) = object("{app_id}/roles", options)
+ scores(app_id, options) = object("{app_id}/scores", options)
+ invited(event_id, options) = object("{event_id}/invited", options)
+ is_invited(event_id, user_id, options) = object("{event_id}/invited/{user_id}", options)
+ attending(event_id, options) = object("{event_id}/attending", options)
+ is_attending(event_id, user_id, options) = object("{event_id}/attending/{user_id}", options)
+ maybe(event_id, options) = object("{event_id}/maybe", options)
+ is_maybe(event_id, user_id, options) = object("{event_id}/maybe/{user_id}", options)
+ declined(event_id, options) = object("{event_id}/declined", options)
+ is_declined(event_id, user_id, options) = object("{event_id}/declined/{user_id}", options)
+ noreply(event_id, options) = object("{event_id}/noreply", options)
+ photos(id, options) = object("{id}/photos", options)
+ picture(id, options) = redirect("{id}/photos", options)
+ friendlist = object
+
}}
Search = {{
@@ -895,10 +1280,18 @@ FbGraph = {{
|> FbLib.add_if_filled("start_time", aux_date(e.start_time), _)
|> FbLib.add_if_filled("end_time", aux_date(e.end_time), _)
+ achievement_to_data(a:FbGraph.achievement) =
+ [("achievement", a.achievement)]
+ |> FbLib.add_if_filled("message", a.message, _)
+
album_to_data(a:FbGraph.album) =
[("name", a.name)]
|> FbLib.add_if_filled("message", a.message, _)
|> FbLib.add_if_filled("location", a.location, _)
|> FbLib.add_if_filled("link", a.link, _)
+ photo_to_form(p:FbGraph.photo) =
+ [{file=("source", p.filename, p.content_type, p.source)}]
+ |> FbLib.add_form_if_filled("message", p.message, _)
+
}}
View
115 lib/stdlib/apis/facebook/lib/lib.opa
@@ -41,28 +41,88 @@ FbLib = {{
/**
* Make a HTTP GET on [path] at [base] with [data]
*/
- fb_get(base, path, data) =
+ @private fb_get_(base, path, data, process) =
final_path = generic_build_path("{base}{path}", data)
- do jlog("fb_get: final_path={final_path}")
+ do jlog("GET {final_path}")
match Uri.of_string(final_path) with
| {none} -> none
- | {some=uri} ->
- match WebClient.Get.try_get(uri) with
- | {failure=_} -> none
- | {success=s} -> {some=s.content}
- end
+ | {some=uri} -> process(WebClient.Get.try_get(uri))
+
+ @private content_only(res) =
+ match res with
+ | {failure=_} -> none
+ | {success=s} -> do jlog("HTTP {s.code}\n{s.content}") {some=s.content}
+
+ // header_get doesn't work on node.
+ find_header(name,headers) =
+ List.fold((h, acc ->
+ if String.has_prefix(name,h)
+ then
+ len = String.length(name)
+ {some=String.sub(len+2,String.length(h)-(len+2),h)}
+ else acc),headers,none)
+
+ @private content_with_type(res) =
+ match res with
+ | {failure=_} -> none
+ | {success=s} ->
+ do jlog("hdrs=\n{String.concat("\n",s.headers)}")
+ content_type = Option.default("unknown/unknown",find_header("content-type",s.headers))
+ location = Option.default("",find_header("location",s.headers))
+ do jlog("HTTP {s.code} type={content_type} location={location}")
+ {some=(content_type,location,s.content)}
+
+ @private redirect(res) =
+ match res with
+ | {failure=_} -> none
+ | {success=s} ->
+ do jlog("HTTP {s.code}")
+ if s.code == 302
+ then find_header("location",s.headers)
+ else none
+
+ fb_get(base, path, data) = fb_get_(base, path, data, content_only)
+ fb_get_ct(base, path, data) = fb_get_(base, path, data, content_with_type)
+ fb_get_redirect(base, path, data) = fb_get_(base, path, data, redirect)
/**
* Make a HTTP POST on [path] at [base] with [data]
*/
fb_post(base, path, data) =
txtdata = API_libs.form_urlencode(data)
+ do jlog("POST {base}{path}\n{txtdata}\n")
match Uri.of_string("{base}{path}") with
| {none} -> none
| {some=uri} ->
match WebClient.Post.try_post(uri,txtdata) with
| {failure=_} -> none
- | {success=s} -> {some=s.content}
+ | {success=s} -> do jlog("HTTP {s.code}\n{s.content}") {some=s.content}
+ end
+
+ @private memdump = (%% BslPervasives.memdump %%: string -> string)
+
+ /**
+ * Make a HTTP POST on [path] at [base] with [forms] form data
+ */
+ fb_post_multi(base, path, forms) =
+ bound = Random.string(20)
+ mimetype = "multipart/form-data; boundary={bound}"
+ forms = List.map((f ->
+ match f with
+ | {form=(name, content)} ->
+ "--{bound}\r\nContent-Disposition: form-data; name=\"{name}\"\r\n\r\n{content}\r\n"
+ | {file=(name, filename, content_type, content)} ->
+ "--{bound}\r\nContent-Disposition: form-data; name=\"{name}\"; filename=\"{filename}\"\r\nContent-Type={content_type}\r\n\r\n{content}\r\n"
+ ),forms)
+ content = some(String.concat("",List.append(forms,["--{bound}--\r\n"])))
+ options = ~{ WebClient.Post.default_options with mimetype content }
+ do jlog("POST {base}{path}\n{memdump(Option.get(content))}\n")
+ match Uri.of_string("{base}{path}") with
+ | {none} -> none
+ | {some=uri} ->
+ match WebClient.Post.try_post_with_options(uri,options) with
+ | {failure=_} -> none
+ | {success=s} -> do jlog("HTTP {s.code}\n{s.content}") {some=s.content}
end
/**
@@ -70,12 +130,13 @@ FbLib = {{
*/
fb_delete(base, path, data) =
final_path = generic_build_path("{base}{path}", data)
+ do jlog("DELETE {final_path}")
match Uri.of_string(final_path) with
| {none} -> none
| {some=uri} ->
match WebClient.Delete.try_delete(uri) with
| {failure=_} -> none
- | {success=s} -> {some=s.content}
+ | {success=s} -> do jlog("HTTP {s.code}\n{s.content}") {some=s.content}
end
/* Generic JSON functions */
@@ -91,12 +152,44 @@ FbLib = {{
| _ -> ""
add_if_filled(name, v, data) =
- if v == "" then data
+ if v == ""
+ then data
else List.add((name, v), data)
+ add_if_filled_opt(name, tostr, v, data) =
+ if Option.is_none(v)
+ then data
+ else List.add((name, tostr(Option.get(v))), data)
+
+ add_if_filled_generic(name, tostr, v, data) =
+ str = tostr(v)
+ if str == ""
+ then data
+ else List.add((name, str), data)
+
+ add_bool_if_filled(name, v:option(bool), data) = add_if_filled_opt(name, Bool.to_string, v, data)
+
+ add_int_if_filled(name, v:option(int), data) = add_if_filled_opt(name, Int.to_string, v, data)
+
+ add_array_if_filled(name, tojson:list('a)->RPC.Json.json, v:list('a), data) =
+ if v == []
+ then data
+ else List.add((name, Json.serialize(tojson(v))), data)
+
+ add_json(name, tojson:'a->RPC.Json.json, v:'a, data) =
+ List.add((name, Json.serialize(tojson(v))), data)
+
+ add_form_if_filled(name, v, data) =
+ if v == ""
+ then data
+ else List.add({form=(name, v)}, data)
+
/* Feed to data */
- @private actions_to_json(actions) : RPC.Json.json =
+ fb_properties_to_json(properties:Facebook.properties) : RPC.Json.json =
+ {List=List.map((p -> {String=Facebook.string_of_property(p)}),properties)}
+
+ actions_to_json(actions) : RPC.Json.json =
aux(l:Facebook.feed_link) : RPC.Json.json =
{Record=[("name", {String=l.text}:RPC.Json.json),
("link", {String=l.href}:RPC.Json.json)]}
Please sign in to comment.
Something went wrong with that request. Please try again.