- Intro
- Class Api
- Functional Api
- Advanced
- Access Control
- Permissions
- OperationStack
- Metrics
- Queries & Filters
- Catalogs & Collections
- Composing Workflows
- Developers
- Legacy
Overview
As organizations use ArcGIS Hub to support community engagement and collaboration, ArcGIS Hub Discussions provides an integrated capability for organizations to meet their varied needs for internal & external collaborative communication around Hub initiatives, content and data.
Hub Discussions will be available in mid-2021 starting with private discussions within Hub Teams. Through the rest of the year, Hub Discussions will expand to support focused public feedback, dataset comments or corrections, and possibly public forums.
Useful links
Common Terms
A Post is created by a User with a reference to a topic and location within datasets, documents, teams, sites, and any Hub resource.
Users can Reply to a Post, or add a Reaction to quickly indicate support or other feeling.
Posts are shared in a Channel. The Channel defines which groups/organizations can participate in the discussion, who can manage posts, and whether the discussion allows public participation.
Table of Contents
- Overview
- Getting Started
- Posts
- Reactions
- Searching Posts
- Channels
- Authentication and Authorization
- In Conclusion
- Useful links
Getting Started
This is a quick introduction to using @esri/hub-discussions
in your own application or service. You can find more details on each topic further below.
Authentication
Authentication is provided using arcgis-rest-js.
import { UserSession } from "@esri/arcgis-rest-auth";
const authentication = new UserSession({
username: "jdoe",
password: "########",
portal: "https://www.arcgis.com/sharing/rest",
});
Creating posts
To create a post use createPost.
The post.discussion
is important as a reference for the Post to some content item. Currently discussion
uses a URI like hub://item/{itemId}
. See Posting about content for details.
import {
createPostV2,
ICreatePost,
ICreatePostParamsV2,
} from "@esri/hub-discussions";
const data: ICreatePost = {
title: "Question about updating trees dataset",
body: "We need to add more details about tree planting date.",
discussion: "hub://dataset/1234",
channelId: "3efabc",
};
const opts: ICreatePostOptions = { authentication, data };
const myPost = await createPostV2(opts);
/*
myPost = IPost {
id: 'abcdefg',
...
}
*/
Posts
A Post is the primary forum for dialogue between users in a Channel. Posts are organized into "threads" using a self-join: a post can have many replies, and a post can belong to a single "parent" post. Posts are required to have a body
, and optionally a title
(perhaps best suited for parent posts). Posts may also encode a content subject (called discussion
) and geography (called geometry
). Posts additionally have a status
, which dictates visibility and can be used by channel managers to employ light-weight moderation. The full data model for posts is described below:
property | type | nullable | description |
---|---|---|---|
body | string | false | Main content of post |
title | string | true | Optional title for post |
status | PostStatus | false | Describes a post's visibility |
discussion | string | true | See the Discussion URI section for details. Note that this attribute is optional. A post with no discussion could be viewed as a "general" post ("nice weather we're having" or "let's grab lunch") made in a channel |
geometry | Geometry | true | Geometry property of GeoJSON spec, projected in WGS84 |
featureGeometry | Geometry | true | Geometry property from related feature of GeoJSON spec, projected in WGS84 |
appInfo | string | true | Used to store additional information about a post in specific app context |
postType | PostType | true | Post type |
id | string (UUID v4) | false | Identifier for the post |
channelId | string (UUID v4) | false | Join field for channel |
parentId | string (UUID v4) | true | Join field for parent post if reply |
Posting about content
At the core of the Hub Discussions API is the ability to create posts and drive conversation about data. This data can be a literal dataset, a feature in a dataset, an attribute of a feature of a dataset, or it could be an item, or a sentence in a pdf. The idea is that anything can be discussed with this platform. The property post.discussion
expects a specifically formatted URI string to encapsulate the following information: the application context the post originated from (i.e. Hub, Urban), content type, content id, and optionally for datasets identification of specific layers, features, and attribute. The format allows posts to target coarsely-grained and highly-specific content. See the URI format and examples below:
# coarsely-grained
<application context>://<content type>/<content id>
# highly-specific
<application context>://dataset/<dataset id>_<layer id>?id=<feature id>&attribute=<feature attribute>
## examples
# commenting on item ab4
hub://item/ab4
# commenting on layer 1 of dataset 3ef
hub://dataset/3ef_1
# commenting on feature #5 of layer 1 of dataset 3ef
hub://dataset/3ef_1?id=5
# commenting on features #5 & #7
hub://dataset/3ef_1?id=5,7
# commenting on the species attribute of layer 1 of dataset 3ef
hub://dataset/3ef_1?attribute=species
# commenting on the species attributet of feature 5 of layer 1 of 3ef dataset
hub://dataset/3ef_1?id=5&attribute=species
Replying to posts
A core tenet of any discussion system is the ability to reply to posts. Reply posts are just posts using createReply, except they have a postId
which links them to the "parent" post. All of the same validation for creating a post is applied when creating a reply.
Additionally, a reply post cannot be more visible than the parent post. That is to say, a user cannot reply to a post from a private channel in a public channel; however, a user can reply to a post from a public channel in a private channel. As such, replies in private channels must be made to the same channel.
import {
createReplyV2,
ICreateReplyParams
IPostOptions,
} from "@esri/hub-discussions";
const data: IPostOptions = {
body: "I disagree, I do not like this dataset",
discussion: "hub://dataset/1234",
};
const opts: ICreateReplyParams = { postId: "abcdefg", authentication, data };
const myReply = await createReplyV2(opts);
/*
myReply = IPost {
id: 'gjahsdkj',
...
}
*/
Reactions
The Reaction model is not central to the objective of enabling in-app communication between ArcGIS Online users, but it offers users the ability to engage with posts in an expressive manner familiar to other popular collaboration platforms (think Slack, GitHub, MS Teams, etc). Reactions encode a user's sentiment ("thumbs up", "crying face", "thinking face", etc) onto a post using emojis or symbols.
Creating reactions
If you want to show support for a post, you can add a thumbs up reaction:
import {
createReactionV2,
ICreateReaction,
ICreateReactionOptions,
PostReaction,
} from "@esri/hub-discussions";
const data: ICreateReaction = {
postId: "abd123",
value: PostReaction.THUMBS_UP,
};
const opts: ICreateReactionOptions = { authentication, data };
const thumbs_up = await createReactionV2(opts);
Searching Posts
Searchable properties are enumerated on the ISearchPosts
interface.
In all of these examples, we created a post about Dataset ID 1234. These posts are linked to that dataset through the data.discussion
property. If we wanted to search for posts about this dataset, the code would look something like this:
import {
searchPostsV2,
ISearchPosts,
ISearchPostsParams,
} from "@esri/hub-discussions";
const data: ISearchPosts = {
discussion: "hub://dataset/1234",
};
const opts: ISearchPostsParams = { authentication, data };
const dataset1234Posts = await searchPostsV2(opts);
/*
dataset1234Posts = IPagedResponse<IPost> {
items: [<IPost>, ...],
total: 3,
nextStart: -1
}
*/
Expanded Search
This would return all posts about dataset 1234 in a general sense. But remember that the discussion
parameter can point to layers, features, and attributes of datasets as well.
What if we wanted to find posts about any and every layer and feature included in the dataset, and not just general posts about the dataset? In the Discussions API, all string parameters utilize PostgreSQL's LIKE operator when performing queries, so the provided value is actually a pattern and can use % and _ characters to match sequences and characters, respectively.
Note that this means any actual percent signs or underscores must be backslash escaped. To return every post about dataset 1234, we can change the example above to:
import {
searchPostsV2,
ISearchPosts,
ISearchPostsParams,
} from "@esri/hub-discussions";
const data: ISearchPosts = {
discussion: "%://dataset/1234%",
};
const opts: ISearchPostsParams = { authentication, data };
const allDataset1234Posts = await searchPostsV2(opts);
/*
allDataset1234Posts = IPagedResponse<IPost> {
items: [<IPost>, ...],
total: 6,
nextStart: -1
}
*/
More Search Examples
The query above will return posts from all application contexts (instead of just hub://
) and about any layer, feature, or attribute of that dataset.
Some other common query data are enumerated below:
import { ISearchPosts } from '@esri/hub-discussions';
// search for posts by jdoe made in hub about dataset 1234
const data: ISearchPosts = {
creator: 'jdoe'
discussion: 'hub://dataset/1234',
};
// search for posts about trees that are replies to post abcdefg
const data: ISearchPosts = {
body: '%trees%',
parents: 'abcdefg'
};
// search for posts about trees in private channels only
const data: ISearchPosts = {
body: '%trees%',
access: 'private'
};
Channels
Discussions are shared with users through Channels -- Channels are the mechanism that determine whether a user can view, reply, or manage a channel's posts.
Channel configuration
Channels include configurations that set the "rules of engagement" for the discourse that occurs within it. These settings are outlined below:
property | type | default | description |
---|---|---|---|
allowAsAnonymous | boolean | false | Enables authenticated users to create posts with author details hidden |
allowReaction | boolean | true | Enables users to react to posts in channel |
allowReply | boolean | true | Enables users to reply to posts |
allowedReactions | PostReaction[] | null | Array of allowed post reactions in channel. If null, all reactions are allowed |
blockWords | string[] | null | Array of words or phrases that should trigger moderation |
defaultPostStatus | PostStatus | "approved" | Enables posts to be reviewed before making them visible to other users |
metadata | IChannelMetadata | null | Metadata associated with the channel |
name | string | Name for the channel | |
softDelete | boolean | true | Enables soft-delete strategy for posts in channels, meaning that DELETE actions flag posts as "deleted" instead of permanent deletion |
channelAcl | IChannelAclPermission[] | Participation configuration for the channel (see next section) |
Channel participation configuration
The channel.channelAcl is an array that defines variations of users, organizations, or the public to participate in the discussion. The level of participation is set by the defined role. A role of readWrite
allows creating, updating, and viewing posts in the channel. A role of read
only allows viewing posts in the channel. A role of manage
allows creating, updating, and viewing posts in the channel, as well as moderation and channel re-configuration.
Each entry in channel.channelAcl
has the following shape:
property | type | description |
---|---|---|
category | AclCategory | Category for the permission (group , org , anonymousUser , authenticatedUser ) Category: user not API supported |
subCategory | AclSubCategory | SubCategory for the permission (admin , member ) Only valid for category: group or org |
key | string | Identifier for the category Only valid for category: group or org or user For category: group - key is the ArcGIS Online Group ID For category: org - key is the ArcGIS Online Organization ID For category: user - key is the user's ArcGIS Online username (not API supported) |
role | Role | Role for the permission (manage , readWrite , read ) Role: owner , moderate , write not API supported |
Channel participation configuration examples
import { IChannel } from '@esri/hub-discussions';
// group channel
const groupChannel: IChannel = {
channelAcl: [
{ category: 'group', subCategory: 'admin', key: 'group_id', role: 'owner' },
{ category: 'group', subCategory: 'member', key: 'group_id', role: 'readWrite' },
];
...
};
// multiple group channel
const groupChannel: IChannel = {
channelAcl: [
{ category: 'group', subCategory: 'admin', key: 'group_id', role: 'owner' },
{ category: 'group', subCategory: 'member', key: 'group_id', role: 'readWrite' },
{ category: 'group', subCategory: 'admin', key: 'group_id_2', role: 'manage' },
{ category: 'group', subCategory: 'member', key: 'group_id_2', role: 'readWrite' },
];
...
};
// group channel with public participation
const groupChannel: IChannel = {
channelAcl: [
{ category: 'group', subCategory: 'admin', key: 'group_id', role: 'owner' },
{ category: 'group', subCategory: 'member', key: 'group_id', role: 'readWrite' },
{ category: 'authenticatedUser', role: 'readWrite' },
];
...
};
// group channel with public view only participation
const groupChannel: IChannel = {
channelAcl: [
{ category: 'group', subCategory: 'admin', key: 'group_id', role: 'owner' },
{ category: 'group', subCategory: 'member', key: 'group_id', role: 'readWrite' },
{ category: 'authenticatedUser', role: 'read' },
];
...
};
// org channel
const orgChannel: IChannel = {
channelAcl: [
{ category: 'org', subCategory: 'admin', key: 'org_id', role: 'owner' },
{ category: 'org', subCategory: 'member', key: 'org_id', role: 'readWrite' },
];
...
};
// org channel with group participation
const orgChannel: IChannel = {
channelAcl: [
{ category: 'org', subCategory: 'admin', key: 'org_id', role: 'owner' },
{ category: 'org', subCategory: 'member', key: 'org_id', role: 'readWrite' },
{ category: 'group', subCategory: 'member', key: 'group_id', role: 'readWrite' },
];
...
};
// org channel with group management
const orgChannel: IChannel = {
channelAcl: [
{ category: 'org', subCategory: 'admin', key: 'org_id', role: 'owner' },
{ category: 'org', subCategory: 'member', key: 'org_id', role: 'readWrite' },
{ category: 'group', subCategory: 'member', key: 'group_id', role: 'manage' },
];
...
};
// org channel with public participation
const orgChannel: IChannel = {
channelAcl: [
{ category: 'org', subCategory: 'admin', key: 'org_id', role: 'owner' },
{ category: 'org', subCategory: 'member', key: 'org_id', role: 'readWrite' },
{ category: 'authenticatedUser', role: 'readWrite' },
];
...
};
// org channel with group management and public participation
const orgChannel: IChannel = {
channelAcl: [
{ category: 'org', subCategory: 'admin', key: 'org_id', role: 'owner' },
{ category: 'org', subCategory: 'member', key: 'org_id', role: 'readWrite' },
{ category: 'group', subCategory: 'member', key: 'group_id', role: 'manage' },
{ category: 'authenticatedUser', role: 'readWrite' },
];
...
};
Moderation
Users in the channelAcl category/subCategory with a role of manage
can moderate posts within the channel. Moderators are granted the ability to hide, obscure, and delete other user's posts at their discretion. Only moderators can update post.status
-- non-moderators can only view posts where post.status
equals 'approved'
.
Authentication and Authorization
All requests to the Hub Discussions API rely on ArcGIS Online for authentication.
In the @esri/hub-discussions
library, authentication is handled using the familiar IHubRequestOptions
interface, and specifically the UserSession
instance attached to it.
A user's access to content stored in the Discussions API is determined by their platform identity -- that is their organization and group memberships and manage roles. As noted above, ArcGIS Online access and permissions are encoded in Channels. Currently, most API methods employ one or many checks comparing Channel specs to the requesting user's identity.
A summary of authorization checks are in the table below (source code: channels, posts):
Permission | Description |
---|---|
create a channel | - create acl entry with category: anonymousUser OR authenticatedUser - user has portal privilege portal:admin:shareToPublic or portal:user:shareToPublic - create acl entry with category: group , subCategory: admin - user is the group owner or admin - create acl entry with category: group , subCategory: member - user is a group member - create acl entry with category: org , subCategory: admin or member - user has portal privilege portal:admin:shareToOrg or portal:user:shareToOrg |
view posts in channel | - an acl entry has category: anonymousUser with role: read - OR acl entry has category: authenticatedUser with role: read OR readWrite - OR acl entry has category: group , subCategory: admin OR member with role read or readWrite or manage , and the user is in the group, as a member or admin - OR acl entry has category: org , subCategory: admin OR member with role read or readWrite or manage , and the user is in the org, as a member or admin |
create post in channel | - acl entry has category: authenticatedUser with role: readWrite - OR acl entry has category: group , subCategory: admin OR member with role readWrite or manage , and the user is in the group, as a member or admin - OR acl entry has category: org , subCategory: admin OR member with role readWrite or manage , and the user is in the org, as a member or admin |
moderate post in channel | - acl entry has category: group , subCategory: admin OR member with role manage , and the user is in the group, as a member or admin - OR acl entry has category: org , subCategory: admin OR member with role manage , and the user is in the org, as a member or admin |
In Conclusion
The Hub Discussions API has only begun to expose the numerous ways users can interact with each other around data and content in Hub. The API is under continuous development. The @esri/hub-discussions
package provides a familar Hub.js interface to the API. There is much more that can be done with the Discussions API and library than what is expressed in this guide. Please add an issue to this repo if you have any questions or if something does not work as expected.