Skip to content

Commit

Permalink
feat: adds getChannelUsersQuery method, bumps to @esri/arcgis-rest-po…
Browse files Browse the repository at this point in the history
…rt… (#1210)

* feat: adds getChannelUsersQuery method, bumps to @esri/[email protected], adds support for sear

affects: @esri/hub-common, @esri/hub-downloads

ISSUES CLOSED: 7720
  • Loading branch information
rweber-esri authored Sep 22, 2023
1 parent 6961943 commit 320d6d8
Show file tree
Hide file tree
Showing 14 changed files with 1,157 additions and 76 deletions.
22 changes: 11 additions & 11 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
"@commitlint/prompt": "^11.0.0",
"@esri/arcgis-rest-auth": "^3.1.1",
"@esri/arcgis-rest-feature-layer": "^3.4.3",
"@esri/arcgis-rest-portal": "^3.5.0",
"@esri/arcgis-rest-portal": "^3.7.0",
"@esri/arcgis-rest-request": "^3.1.1",
"@esri/arcgis-rest-service-admin": "^3.6.0",
"@esri/arcgis-rest-types": "^3.1.1",
Expand Down
176 changes: 173 additions & 3 deletions packages/common/src/discussions/utils.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,22 @@
import { IGroup, IItem } from "@esri/arcgis-rest-types";
import { IHubContent, IHubItemEntity } from "../core";
import { CANNOT_DISCUSS } from "./constants";
import { IRequestOptions } from "@esri/arcgis-rest-request";
import { updateItem, updateGroup } from "@esri/arcgis-rest-portal";
import { IUserRequestOptions } from "@esri/arcgis-rest-auth";
import {
AclCategory,
AclSubCategory,
IChannel,
IChannelAclPermission,
SharingAccess,
} from "./api/types";
import {
IFilter,
IHubSearchOptions,
IHubSearchResponse,
IHubSearchResult,
IPredicate,
IQuery,
hubSearch,
} from "../search";

/**
* Utility to determine if a given IGroup, IItem, IHubContent, or IHubItemEntity
Expand Down Expand Up @@ -35,3 +48,160 @@ export function setDiscussableKeyword(
}
return updatedTypeKeywords;
}

/**
* Determines if the given channel is considered to be a `public` channel, supporting both
* legacy permissions and V2 ACL model.
* @param channel An IChannel record
* @returns true if the channel is considered `public`
*/
export function isPublicChannel(channel: IChannel): boolean {
return channel.channelAcl
? channel.channelAcl.some(
({ category }) => category === AclCategory.AUTHENTICATED_USER
)
: channel.access === SharingAccess.PUBLIC;
}

/**
* Determines if the given channel is considered to be an `org` channel, supporting both
* legacy permissions and V2 ACL model.
* @param channel An IChannel record
* @returns true if the channel is considered `org`
*/
export function isOrgChannel(channel: IChannel): boolean {
return channel.channelAcl
? !isPublicChannel(channel) &&
channel.channelAcl.some(
({ category, subCategory }) =>
category === AclCategory.ORG &&
subCategory === AclSubCategory.MEMBER
)
: channel.access === SharingAccess.ORG;
}

/**
* Determines if the given channel is considered to be a `private` channel, supporting both
* legacy permissions and V2 ACL model.
* @param channel An IChannel record
* @returns true if the channel is considered `private`
*/
export function isPrivateChannel(channel: IChannel): boolean {
return !isPublicChannel(channel) && !isOrgChannel(channel);
}

/**
* Determines the given channel's access, supporting both legacy permissions and V2 ACL
* model.
* @param channel An IChannel record
* @returns `public`, `org` or `private`
*/
export function getChannelAccess(channel: IChannel): SharingAccess {
let access = SharingAccess.PRIVATE;
if (isPublicChannel(channel)) {
access = SharingAccess.PUBLIC;
} else if (isOrgChannel(channel)) {
access = SharingAccess.ORG;
}
return access;
}

/**
* Returns an array of org ids configured for the channel, supporting both legacy permissions
* and V2 ACL model.
* @param channel An IChannel record
* @returns an array of org ids for the given channel
*/
export function getChannelOrgIds(channel: IChannel): string[] {
return channel.channelAcl
? channel.channelAcl.reduce(
(acc, permission) =>
permission.category === AclCategory.ORG &&
permission.subCategory === AclSubCategory.MEMBER
? [...acc, permission.key]
: acc,
[]
)
: channel.orgs;
}

/**
* Returns an array of group ids configured for the channel, supporting both legacy permissions
* and V2 ACL model.
* @param channel An IChannel record
* @returns an array of group ids for the given channel
*/
export function getChannelGroupIds(channel: IChannel): string[] {
return channel.channelAcl
? channel.channelAcl.reduce(
(acc, permission) =>
permission.category === AclCategory.GROUP &&
permission.subCategory === AclSubCategory.MEMBER
? [...acc, permission.key]
: acc,
[]
)
: channel.groups;
}

/**
* A utility method used to build an IQuery to search for users that are permitted to be at-mentioned for the given channel.
* @param input An array of strings to search for. Each string is mapped to `username` and `fullname`, filters as an OR condition
* @param channel An IChannel record
* @param currentUsername The currently authenticated user's username
* @param options An IHubSearchOptions object
* @returns a promise that resolves an IHubSearchResponse<IHubSearchResult>
*/
export function getChannelUsersQuery(
inputs: string[],
channel: IChannel,
currentUsername?: string
): IQuery {
const groupIds = getChannelGroupIds(channel);
const orgIds = getChannelOrgIds(channel);
const groupsPredicate = { group: groupIds };
let filters: IFilter[];
if (isPublicChannel(channel)) {
filters = [
{
operation: "OR",
predicates: [{ orgid: { from: "0", to: "{" } }],
},
];
} else if (isOrgChannel(channel)) {
const additional = groupIds.length ? [groupsPredicate] : [];
filters = [
{
operation: "OR",
predicates: [{ orgid: orgIds }, ...additional],
},
];
} else {
filters = [
{
operation: "AND",
predicates: [groupsPredicate],
},
];
}
if (currentUsername) {
filters.push({
operation: "AND",
predicates: [{ username: { not: currentUsername } }],
});
}
const query: IQuery = {
targetEntity: "communityUser",
filters: [
{
operation: "OR",
predicates: inputs.reduce<IPredicate[]>(
(acc, input) => [...acc, { username: input }, { fullname: input }],
[]
),
},
...filters,
],
};
return query;
}
Original file line number Diff line number Diff line change
Expand Up @@ -218,5 +218,9 @@ async function memberToSearchResult(
}
});

return enrichUserSearchResult(user, include, requestOptions);
return enrichUserSearchResult(
user,
["org.name as OrgName", ...include],
requestOptions
);
}
Loading

0 comments on commit 320d6d8

Please sign in to comment.