diff --git a/README.md b/README.md index 96d7e83..9d71747 100644 --- a/README.md +++ b/README.md @@ -14,106 +14,107 @@ To stay updated on new releases or iterations, join the email list [here](https: - [Authentication](#authentication) - [Errors](#errors) - [Paging](#paging) + - [search_limited_to](#search_limited_to) - [Comparison filters](#comparison-filters) - [OSDI compliance](#osdi-compliance) - [Organizations](#organizations) - [Organization object](#organization-object) - [List all organizations](#list-all-organizations) - - [Request](#request) - - [Request params](#request-params) - - [Response](#response) + - [Request](#request) + - [Request params](#request-params) + - [Response](#response) - [List all the organizations promoted by an organization](#list-all-the-organizations-promoted-by-an-organization) - - [Request](#request-1) - - [Request params](#request-params-1) - - [Response](#response-1) + - [Request](#request-1) + - [Request params](#request-params-1) + - [Response](#response-1) - [Events](#events) - [Event object](#event-object) - - [Timeslot](#timeslot) - - [Location](#location) - - [Contact](#contact) - - [Tag](#tag) - - [EventCampaign](#eventcampaign) - - [Deleted Event](#deleted-event) - - [List all public events](#list-all-public-events) - - [Request](#request-2) - - [Request params](#request-params-2) - - [Response](#response-2) + - [Timeslot](#timeslot) + - [Location](#location) + - [Contact](#contact) + - [Tag](#tag) + - [EventCampaign](#eventcampaign) + - [Deleted Event](#deleted-event) + - [[DEPRECATED] List all public events](#deprecated-list-all-public-events) + - [Request](#request-2) + - [Request params](#request-params-2) + - [Response](#response-2) - [Get a public event](#get-a-public-event) - - [Request](#request-3) - - [Request params](#request-params-3) - - [Response](#response-3) + - [Request](#request-3) + - [Request params](#request-params-3) + - [Response](#response-3) - [List organization events](#list-organization-events) - - [Request](#request-4) - - [Request params](#request-params-4) - - [Response](#response-4) + - [Request](#request-4) + - [Request params](#request-params-4) + - [Response](#response-4) - [Get an organization event](#get-an-organization-event) - - [Request](#request-5) - - [Request params](#request-params-5) - - [Response](#response-5) - - [List deleted public events](#list-deleted-public-events) - - [Request](#request-6) - - [Request params](#request-params-6) - - [Response](#response-6) + - [Request](#request-5) + - [Request params](#request-params-5) + - [Response](#response-5) + - [[DEPRECATED] List deleted public events](#deprecated-list-deleted-public-events) + - [Request](#request-6) + - [Request params](#request-params-6) + - [Response](#response-6) - [List deleted organization events](#list-deleted-organization-events) - - [Request](#request-7) - - [Request params](#request-params-7) - - [Response](#response-7) + - [Request](#request-7) + - [Request params](#request-params-7) + - [Response](#response-7) - [Create event](#create-event) - - [Request](#request-8) - - [Request body](#request-body) - - [Request body example](#request-body-example) - - [Response](#response-8) + - [Request](#request-8) + - [Request body](#request-body) + - [Request body example](#request-body-example) + - [Response](#response-8) - [Update event](#update-event) - - [Request](#request-9) - - [Request params](#request-params-8) - - [Request body](#request-body-1) - - [Request body example](#request-body-example-1) - - [Response](#response-9) + - [Request](#request-9) + - [Request params](#request-params-8) + - [Request body](#request-body-1) + - [Request body example](#request-body-example-1) + - [Response](#response-9) - [Delete event](#delete-event) - - [Request](#request-10) - - [Request params](#request-params-9) - - [Response](#response-10) + - [Request](#request-10) + - [Request params](#request-params-9) + - [Response](#response-10) - [People](#people) - [Person object](#person-object) - - [Email](#email) - - [Phone](#phone) - - [Address](#address) + - [Email](#email) + - [Phone](#phone) + - [Address](#address) - [List organization people](#list-organization-people) - - [Request](#request-11) - - [Request params](#request-params-10) - - [Response](#response-11) + - [Request](#request-11) + - [Request params](#request-params-10) + - [Response](#response-11) - [Attendances](#attendances) - [Attendance object](#attendance-object) - - [Referrer](#referrer) + - [Referrer](#referrer) - [Get an organization attendance](#get-an-organization-attendance) - - [Request](#request-12) - - [Response](#response-12) + - [Request](#request-12) + - [Response](#response-12) - [List organization attendances](#list-organization-attendances) - - [Request](#request-13) - - [Request params](#request-params-11) - - [Response](#response-13) + - [Request](#request-13) + - [Request params](#request-params-11) + - [Response](#response-13) - [List organization attendances for an event](#list-organization-attendances-for-an-event) - - [Request](#request-14) - - [Response](#response-14) + - [Request](#request-14) + - [Response](#response-14) - [Create organization event attendance](#create-organization-event-attendance) - - [Request](#request-15) - - [Request body](#request-body-2) - - [Person attendance object](#person-attendance-object) - - [Referrer object](#referrer-object) - - [Request body example](#request-body-example-2) - - [Response](#response-15) - - [Response body example](#response-body-example) + - [Request](#request-15) + - [Request body](#request-body-2) + - [Person attendance object](#person-attendance-object) + - [Referrer object](#referrer-object) + - [Request body example](#request-body-example-2) + - [Response](#response-15) + - [Response body example](#response-body-example) - [Affiliation](#affiliation) - [Create organization affiliations](#create-organization-affiliations) - - [Request](#request-16) - - [Request body](#request-body-3) - - [Request body example](#request-body-example-3) - - [Response](#response-16) + - [Request](#request-16) + - [Request body](#request-body-3) + - [Request body example](#request-body-example-3) + - [Response](#response-16) - [Images](#images) - [Upload images](#upload-images) - - [Request](#request-17) - - [Request body](#request-body-4) - - [Response](#response-17) + - [Request](#request-17) + - [Request body](#request-body-4) + - [Response](#response-17) - [Changelog](#changelog) # Overview @@ -164,6 +165,17 @@ Here’s an example error response for `/v1/events?organization_id=hello`: All endpoints support paging, using the query params `per_page` and `page`. `per_page` defaults to 25. In the response, `count` describes the number of total objects, so `ceiling(count / per_page)` gives the total number of pages. Also, for convenience, `next` and `previous` will be links to the next and previous pages respectively. +### `search_limited_to` + +Some endpoints will hit our search backend when filtered on many parameters at +once (like our our [List Organization Events](#list-organization-events) +endpoint), which limits the total number of objects we can return for a given +search. If that happens, we will return a `search_limited_to` field in the +response which will be an integer with the total number of objects that the +endpoint will return, usually 1000. This number may not correspond to the +`count` field, so we advise consumers consult this field to determine whether +their request will be limited. + ## Comparison filters Some endpoints will allow for filtering by comparing with a field within an object. In those cases, the format will be `field_name=cmp_####`. For example, to filter out events before Jan 1 2018 GMT, you can include `timeslot_start=gte_1514764800`. Multiple may also be used, e.g. `timeslot_start=gte_1514764800×lot_start=lt_1515110400`. The comparison operators are ≥ `gte`, > `gt`, ≤ `lte`, < `lt`. More may be added in the future. @@ -371,9 +383,15 @@ When [creating events](#create-event), note that `owner_user_id` should not be s | `id` | int | | | `deleted_date` | int | Unix timestamp | -## List all public events +## **[DEPRECATED]** List all public events -Status: LIVE +**DEPRECATION NOTICE: This endpoint is no longer supported, and will soon function as +an alias for [List Organization Events](#list-organization-events) with +`organization_id` = `1`. See our [Deprecation +Notice](https://github.com/mobilizeamerica/api/pull/64) for details.** + + +Status: DEPRECATED Fetch all public events on the platform. To list an organization’s private events, send an authenticated request to the [organization events list](#list-organization-events) endpoint instead. @@ -560,13 +578,17 @@ need to send an authenticated request to see that data. - `updated_since`: Unix timestamp to filter by Events’ `modified_date` - `timeslot_start`: Comparison to filter by Events' Timeslots' start date. Will only return Timeslots on those Events that meet the filter conditions - `timeslot_end`: Comparison to filter by Events' Timeslots' end date. Will only return Timeslots on those Events that meet the filter conditions -- `zipcode`: Zipcode to filter by Events' Locations' postal code. If present, will return Events sorted by distance from zipcode. When zipcode is provided, virtual events will not be returned. To improve performance, query results are limited to a maximum of 1000. -- `max_dist`: Maximum distance (in miles) to filter by Events' Locations' distance from provided zipcode. - `visibility`: Type of event visibility to filter by; either `PUBLIC` or `PRIVATE`. Private events will only be returned if `visibility=PRIVATE` is specified, the calling user is authenticated, and the user has permission to view the given organization's private events in the dashboard. If `visibility=PRIVATE` is specified and the calling user does not have permission, a 403 error is returned. Defaults to public events only. Note also that both can be specified, ie `visibility=PRIVATE&visibility=PUBLIC`. -- `exclude_full`: Boolean; whether to filter out full Timeslots (and Events, if all of an Event's Timeslots are full), e.g. `exclude_full=true` -- `is_virtual`: Optional boolean, e.g. `is_virtual=false` will return only in-person events, while `is_virtual=true` will return only virtual events. If excluded, return virtual and in-person events. Note that providing a `zipcode` also implies `is_virtual=false`. -- `event_types`: One or more event types to filter to (see possible values in `event_type` on the [Event object](#event-object)). If multiple, should be supplied as multiple query params, e.g. `event_types=CANVASS&event_types=PHONE_BANK`. -- `tag_id`: One or more Tag IDs to filter to. If multiple, should be supplied as multiple query params, e.g., `tag_id=1&tag_id=2`, etc. +- `zipcode`*: Zipcode to filter by Events' Locations' postal code. If present, will return Events sorted by distance from zipcode. When zipcode is provided, virtual events will not be returned. To improve performance, query results are limited to a maximum of 1000. +- `max_dist`*: Maximum distance (in miles) to filter by Events' Locations' distance from provided zipcode. +- `exclude_full`*: Boolean; whether to filter out full Timeslots (and Events, if all of an Event's Timeslots are full), e.g. `exclude_full=true` +- `is_virtual`*: Optional boolean, e.g. `is_virtual=false` will return only in-person events, while `is_virtual=true` will return only virtual events. If excluded, return virtual and in-person events. Note that providing a `zipcode` also implies `is_virtual=false`. +- `event_types`*: One or more event types to filter to (see possible values in `event_type` on the [Event object](#event-object)). If multiple, should be supplied as multiple query params, e.g. `event_types=CANVASS&event_types=PHONE_BANK`. +- `tag_id`*: One or more Tag IDs to filter to. If multiple, should be supplied as multiple query params, e.g., `tag_id=1&tag_id=2`, etc. + +* The presence of any request params marked with a * will trigger our search +backend, which will limit the total results to the value provided in the +[`search_limited_to`](#search_limited_to) field ### Response `data` is an array of Event objects. @@ -709,9 +731,15 @@ None }, } -## List deleted public events +## **[DEPRECATED]** List deleted public events -Status: LIVE +**DEPRECATION NOTICE: This endpoint is no longer supported, and now functions +as an alias for [List Deleted Organization +Events](#list-deleted-organization-events) with `organization_id` = `1`. See +our [Deprecation Notice](https://github.com/mobilizeamerica/api/pull/64) for +details.** + +Status: DEPRECATED Fetch deleted public events on the platform. @@ -724,8 +752,6 @@ Requires authentication: No - `organization_id`: One or more Organization IDs to filter to. If multiple, should be supplied as multiple query params, e.g., `organization_id=1&organization_id=2`, etc. - `updated_since`: Unix timestamp to filter by Events’ `modified_date` -- `zipcode`: Zipcode to filter by Events' Locations' postal code. If present, will return Events sorted by distance from zipcode. When zipcode is provided, virtual events will not be returned. -- `max_dist`: Maximum distance (in miles) to filter by Events' Locations' distance from provided zipcode. ### Response `data` is an array of Deleted Event objects. @@ -746,8 +772,6 @@ While authentication is not required for this endpoint, if it’s not provided t ### Request params - `updated_since`: Unix timestamp to filter by Events’ `modified_date` -- `zipcode`: Zipcode to filter by Events' Locations' postal code. If present, will return Events sorted by distance from zipcode. When zipcode is provided, virtual events will not be returned. -- `max_dist`: Maximum distance (in miles) to filter by Events' Locations' distance from provided zipcode. - `visibility`: Type of event visibility to filter by; either `PUBLIC` or `PRIVATE`. Private events will only be returned if `visibility=PRIVATE` is specified, the calling user is authenticated, and the user has permission to view the given organization's private events in the dashboard. If `visibility=PRIVATE` is specified and the calling user does not have permission, no events will be returned. Defaults to public events only. Note also that both can be specified, ie `visibility=PRIVATE&visibility=PUBLIC`. ### Response @@ -1312,6 +1336,12 @@ Requires authentication: Yes `data` contains the Mobilize-hosted image URL, which can then be used as the `featured_image_url` when creating or updating events. # Changelog +**2020-03-10** +- Notice of upcoming changes to the API. See [details here](https://github.com/mobilizeamerica/api/pull/64). + - Deprecate [List All Public Events](#list-all-public-events) and [List Deleted Public Events](#list-deleted-public-events) + - Remove unused `max_dist` and `zipcode` filters from our List Deleted Organization Events + - Add explainer on `search_limited_to` and fields that trigger it + **2020-03-11** - Include `blocked_date` in [Person objects](#person-object). Available on [list organization people](#list-organization-people) and attendances endpoints. diff --git a/gh-md-toc b/gh-md-toc new file mode 100755 index 0000000..7ea4b57 --- /dev/null +++ b/gh-md-toc @@ -0,0 +1,287 @@ +#!/usr/bin/env bash + +# +# Steps: +# +# 1. Download corresponding html file for some README.md: +# curl -s $1 +# +# 2. Discard rows where no substring 'user-content-' (github's markup): +# awk '/user-content-/ { ... +# +# 3.1 Get last number in each row like ' ... sitemap.js.*<\/h/)+2, RLENGTH-5) +# +# 5. Find anchor and insert it inside "(...)": +# substr($0, match($0, "href=\"[^\"]+?\" ")+6, RLENGTH-8) +# + +gh_toc_version="0.6.2" + +gh_user_agent="gh-md-toc v$gh_toc_version" + +# +# Download rendered into html README.md by its url. +# +# +gh_toc_load() { + local gh_url=$1 + + if type curl &>/dev/null; then + curl --user-agent "$gh_user_agent" -s "$gh_url" + elif type wget &>/dev/null; then + wget --user-agent="$gh_user_agent" -qO- "$gh_url" + else + echo "Please, install 'curl' or 'wget' and try again." + exit 1 + fi +} + +# +# Converts local md file into html by GitHub +# +# ➥ curl -X POST --data '{"text": "Hello world github/linguist#1 **cool**, and #1!"}' https://api.github.com/markdown +#

Hello world github/linguist#1 cool, and #1!

'" +gh_toc_md2html() { + local gh_file_md=$1 + URL=https://api.github.com/markdown/raw + + if [ ! -z "$GH_TOC_TOKEN" ]; then + TOKEN=$GH_TOC_TOKEN + else + TOKEN_FILE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/token.txt" + if [ -f "$TOKEN_FILE" ]; then + TOKEN="$(cat $TOKEN_FILE)" + fi + fi + if [ ! -z "${TOKEN}" ]; then + AUTHORIZATION="--header \"Authorization: token ${TOKEN}\"" + fi + + # echo $URL 1>&2 + OUTPUT="$(curl -s --user-agent "$gh_user_agent" \ + --data-binary @"$gh_file_md" -H "Content-Type:text/plain" \ + ${AUTHORIZATION} \ + $URL)" + + if [ "$?" != "0" ]; then + echo "XXNetworkErrorXX" + fi + if [ "$(echo "${OUTPUT}" | awk '/API rate limit exceeded/')" != "" ]; then + echo "XXRateLimitXX" + else + echo "${OUTPUT}" + fi +} + + +# +# Is passed string url +# +gh_is_url() { + case $1 in + https* | http*) + echo "yes";; + *) + echo "no";; + esac +} + +# +# TOC generator +# +gh_toc(){ + local gh_src=$1 + local gh_src_copy=$1 + local gh_ttl_docs=$2 + local need_replace=$3 + + if [ "$gh_src" = "" ]; then + echo "Please, enter URL or local path for a README.md" + exit 1 + fi + + + # Show "TOC" string only if working with one document + if [ "$gh_ttl_docs" = "1" ]; then + + echo "Table of Contents" + echo "=================" + echo "" + gh_src_copy="" + + fi + + if [ "$(gh_is_url "$gh_src")" == "yes" ]; then + gh_toc_load "$gh_src" | gh_toc_grab "$gh_src_copy" + if [ "${PIPESTATUS[0]}" != "0" ]; then + echo "Could not load remote document." + echo "Please check your url or network connectivity" + exit 1 + fi + if [ "$need_replace" = "yes" ]; then + echo + echo "!! '$gh_src' is not a local file" + echo "!! Can't insert the TOC into it." + echo + fi + else + local rawhtml=$(gh_toc_md2html "$gh_src") + if [ "$rawhtml" == "XXNetworkErrorXX" ]; then + echo "Parsing local markdown file requires access to github API" + echo "Please make sure curl is installed and check your network connectivity" + exit 1 + fi + if [ "$rawhtml" == "XXRateLimitXX" ]; then + echo "Parsing local markdown file requires access to github API" + echo "Error: You exceeded the hourly limit. See: https://developer.github.com/v3/#rate-limiting" + TOKEN_FILE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/token.txt" + echo "or place GitHub auth token here: ${TOKEN_FILE}" + exit 1 + fi + local toc=`echo "$rawhtml" | gh_toc_grab "$gh_src_copy"` + echo "$toc" + if [ "$need_replace" = "yes" ]; then + if grep -Fxq "" $gh_src && grep -Fxq "" $gh_src; then + echo "Found markers" + else + echo "You don't have or in your file...exiting" + exit 1 + fi + local ts="<\!--ts-->" + local te="<\!--te-->" + local dt=`date +'%F_%H%M%S'` + local ext=".orig.${dt}" + local toc_path="${gh_src}.toc.${dt}" + local toc_footer="" + # http://fahdshariff.blogspot.ru/2012/12/sed-mutli-line-replacement-between-two.html + # clear old TOC + sed -i${ext} "/${ts}/,/${te}/{//!d;}" "$gh_src" + # create toc file + echo "${toc}" > "${toc_path}" + echo -e "\n${toc_footer}\n" >> "$toc_path" + # insert toc file + if [[ "`uname`" == "Darwin" ]]; then + sed -i "" "/${ts}/r ${toc_path}" "$gh_src" + else + sed -i "/${ts}/r ${toc_path}" "$gh_src" + fi + echo + echo "!! TOC was added into: '$gh_src'" + echo "!! Origin version of the file: '${gh_src}${ext}'" + echo "!! TOC added into a separate file: '${toc_path}'" + echo + fi + fi +} + +# +# Grabber of the TOC from rendered html +# +# $1 — a source url of document. +# It's need if TOC is generated for multiple documents. +# +gh_toc_grab() { + # if closed is on the new line, then move it on the prev line + # for example: + # was: The command foo1 + # + # became: The command foo1 + sed -e ':a' -e 'N' -e '$!ba' -e 's/\n<\/h/<\/h/g' | + # find strings that corresponds to template + grep -E -o '//g' | sed 's/<\/code>//g' | + # now all rows are like: + # ... .*<\/h/)+2, RLENGTH-5) + href = substr($0, match($0, "href=\"[^\"]+?\"")+6, RLENGTH-7) + print sprintf("%*s", level*3, " ") "* [" text "](" gh_url href ")" }' | + sed 'y/+/ /; s/%/\\x/g')" +} + +# +# Returns filename only from full path or url +# +gh_toc_get_filename() { + echo "${1##*/}" +} + +# +# Options hendlers +# +gh_toc_app() { + local need_replace="no" + + if [ "$1" = '--help' ] || [ $# -eq 0 ] ; then + local app_name=$(basename "$0") + echo "GitHub TOC generator ($app_name): $gh_toc_version" + echo "" + echo "Usage:" + echo " $app_name [--insert] src [src] Create TOC for a README file (url or local path)" + echo " $app_name - Create TOC for markdown from STDIN" + echo " $app_name --help Show help" + echo " $app_name --version Show version" + return + fi + + if [ "$1" = '--version' ]; then + echo "$gh_toc_version" + echo + echo "os: `lsb_release -d | cut -f 2`" + echo "kernel: `cat /proc/version`" + echo "shell: `$SHELL --version`" + echo + for tool in curl wget grep awk sed; do + printf "%-5s: " $tool + echo `$tool --version | head -n 1` + done + return + fi + + if [ "$1" = "-" ]; then + if [ -z "$TMPDIR" ]; then + TMPDIR="/tmp" + elif [ -n "$TMPDIR" -a ! -d "$TMPDIR" ]; then + mkdir -p "$TMPDIR" + fi + local gh_tmp_md + gh_tmp_md=$(mktemp $TMPDIR/tmp.XXXXXX) + while read input; do + echo "$input" >> "$gh_tmp_md" + done + gh_toc_md2html "$gh_tmp_md" | gh_toc_grab "" + return + fi + + if [ "$1" = '--insert' ]; then + need_replace="yes" + shift + fi + + for md in "$@" + do + echo "" + gh_toc "$md" "$#" "$need_replace" + done + + echo "" + echo "Created by [gh-md-toc](https://github.com/ekalinin/github-markdown-toc)" +} + +# +# Entry point +# +gh_toc_app "$@"