-
Notifications
You must be signed in to change notification settings - Fork 13
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: add/update associations business logic (#1370)
This PR exposes the following set of top-level association utils: requestAssociation, acceptAssociation, breakAssociation, getAssociatedEntitiesQuery, getPendingEntitiesQuery, getRequestingEntitiesQuery, getAvailableToRequestEntitiesQuery, getWellKnownAssociationsCatalog, getAssociationStats
- Loading branch information
1 parent
1931b30
commit 75abd91
Showing
76 changed files
with
2,687 additions
and
336 deletions.
There are no files selected for viewing
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
import { unshareItemWithGroup } from "@esri/arcgis-rest-portal"; | ||
import { IArcGISContext } from "../ArcGISContext"; | ||
import { getProp } from "../objects"; | ||
import { fetchHubEntity } from "../core"; | ||
import { HubEntity, HubEntityType } from "../core/types"; | ||
import { getTypeFromEntity } from "../core/getTypeFromEntity"; | ||
import { updateHubEntity } from "../core/updateHubEntity"; | ||
import { getAssociationHierarchy } from "./internal/getAssociationHierarchy"; | ||
import { isAssociationSupported } from "./internal/isAssociationSupported"; | ||
import { removeAssociationKeyword } from "./internal/removeAssociationKeyword"; | ||
|
||
/** | ||
* When an entity decides it wants to "disconnect" itself | ||
* from an existing association, half of the association | ||
* "connection" is broken. | ||
* | ||
* from the parent's perspective: the parent removes | ||
* the child from its association group | ||
* | ||
* From the child's perspective: the child removes | ||
* the parent reference (ref|<parentType>|<parentID>) | ||
* from its typeKeywords | ||
* | ||
* @param entity - entity initiating the disconnection | ||
* @param type - type of the entity the initiating entity wants to disconnect from | ||
* @param id - id of the entity the initiating entity wants to disconnect from | ||
* @param context - contextual portal and auth information | ||
*/ | ||
export const breakAssociation = async ( | ||
entity: HubEntity, | ||
associationType: HubEntityType, | ||
id: string, | ||
context: IArcGISContext | ||
): Promise<void> => { | ||
const entityType = getTypeFromEntity(entity); | ||
const isSupported = isAssociationSupported(entityType, associationType); | ||
|
||
if (!isSupported) { | ||
throw new Error( | ||
`breakAssociation: Association between ${entityType} and ${associationType} is not supported.` | ||
); | ||
} | ||
|
||
const associationHierarchy = getAssociationHierarchy(entityType); | ||
const isParent = associationHierarchy.children.includes(associationType); | ||
|
||
if (isParent) { | ||
const associationGroupId = getProp(entity, "associations.groupId"); | ||
const { owner } = await fetchHubEntity(associationType, id, context); | ||
try { | ||
await unshareItemWithGroup({ | ||
id, | ||
groupId: associationGroupId, | ||
authentication: context.session, | ||
owner, | ||
}); | ||
} catch (error) { | ||
throw new Error( | ||
`breakAssociation: there was an error unsharing ${id} from ${associationGroupId}: ${error}` | ||
); | ||
} | ||
} else { | ||
entity.typeKeywords = removeAssociationKeyword( | ||
entity.typeKeywords, | ||
associationType, | ||
id | ||
); | ||
await updateHubEntity(entityType, entity, context); | ||
} | ||
}; |
51 changes: 51 additions & 0 deletions
51
packages/common/src/associations/getAssociatedEntitiesQuery.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
import { getTypeFromEntity } from "../core/getTypeFromEntity"; | ||
import { IQuery } from "../search/types"; | ||
import { HubEntity, HubEntityType } from "../core/types"; | ||
import { getAssociationHierarchy } from "./internal/getAssociationHierarchy"; | ||
import { isAssociationSupported } from "./internal/isAssociationSupported"; | ||
import { getIncludesAndReferencesQuery } from "./internal/getIncludesAndReferencesQuery"; | ||
import { IArcGISContext } from "../ArcGISContext"; | ||
|
||
/** | ||
* Associated entities are those which have mutually | ||
* "agreed" to be connected with one another. They | ||
* require a two-way "connection" between parent/child: | ||
* | ||
* parent: "includes" the child in its association query | ||
* child: "references" the parent via a typeKeyword of | ||
* the form ref|<parentType>|<parentID> | ||
* | ||
* The following returns a query to view an entity's | ||
* associations with another entity type | ||
* | ||
* @param entity - Hub entity | ||
* @param associationType - entity type to query for | ||
* @param context - contextual auth and portal information | ||
* @returns {IQuery} | ||
*/ | ||
export const getAssociatedEntitiesQuery = async ( | ||
entity: HubEntity, | ||
associationType: HubEntityType, | ||
context: IArcGISContext | ||
): Promise<IQuery> => { | ||
const entityType = getTypeFromEntity(entity); | ||
const isSupported = isAssociationSupported(entityType, associationType); | ||
|
||
if (!isSupported) { | ||
throw new Error( | ||
`getAssociatedEntitiesQuery: Association between ${entityType} and ${associationType} is not supported.` | ||
); | ||
} | ||
|
||
const associationHierarchy = getAssociationHierarchy(entityType); | ||
const isParent = associationHierarchy.children.includes(associationType); | ||
|
||
const query = await getIncludesAndReferencesQuery( | ||
entity, | ||
associationType, | ||
isParent, | ||
context | ||
); | ||
|
||
return query; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,76 @@ | ||
import { IArcGISContext } from "../ArcGISContext"; | ||
import { getTypeFromEntity } from "../core/getTypeFromEntity"; | ||
import { HubEntity, HubEntityType } from "../core/types"; | ||
import { IQuery } from "../search/types"; | ||
import { hubSearch } from "../search/hubSearch"; | ||
import { getAssociatedEntitiesQuery } from "./getAssociatedEntitiesQuery"; | ||
import { getPendingEntitiesQuery } from "./getPendingEntitiesQuery"; | ||
import { getRequestingEntitiesQuery } from "./getRequestingEntitiesQuery"; | ||
import { getAssociationHierarchy } from "./internal/getAssociationHierarchy"; | ||
import { isAssociationSupported } from "./internal/isAssociationSupported"; | ||
import { IAssociationStats } from "./types"; | ||
|
||
/** | ||
* get an entity's association stats - # of associated, pending, | ||
* requesting, included/referenced entities | ||
* | ||
* @param entity - Hub entity | ||
* @param associationType - entity type to query for | ||
* @param context - contextual auth and portal information | ||
* @returns | ||
*/ | ||
export const getAssociationStats = async ( | ||
entity: HubEntity, | ||
associationType: HubEntityType, | ||
context: IArcGISContext | ||
): Promise<IAssociationStats> => { | ||
let stats: IAssociationStats; | ||
const entityType = getTypeFromEntity(entity); | ||
const isSupported = isAssociationSupported(entityType, associationType); | ||
|
||
if (!isSupported) { | ||
throw new Error( | ||
`getAssociationStats: Association between ${entityType} and ${associationType} is not supported.` | ||
); | ||
} | ||
|
||
const associationHierarchy = getAssociationHierarchy(entityType); | ||
const isParent = associationHierarchy.children.includes(associationType); | ||
|
||
stats = { | ||
associated: 0, | ||
pending: 0, | ||
requesting: 0, | ||
...(isParent ? { included: 0 } : { referenced: 0 }), | ||
}; | ||
|
||
try { | ||
const queries = await Promise.all([ | ||
getAssociatedEntitiesQuery(entity, associationType, context), | ||
getPendingEntitiesQuery(entity, associationType, context), | ||
getRequestingEntitiesQuery(entity, associationType, context), | ||
]); | ||
|
||
const [{ total: associated }, { total: pending }, { total: requesting }] = | ||
await Promise.all( | ||
queries.map((query: IQuery) => { | ||
return hubSearch(query, { | ||
requestOptions: context.hubRequestOptions, | ||
}); | ||
}) | ||
); | ||
|
||
stats = { | ||
associated, | ||
pending, | ||
requesting, | ||
...(isParent | ||
? { included: associated + pending } | ||
: { referenced: associated + pending }), | ||
}; | ||
} catch (error) { | ||
return stats; | ||
} | ||
|
||
return stats; | ||
}; |
Oops, something went wrong.