Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add/update associations business logic #1370

Merged
merged 57 commits into from
Jan 9, 2024
Merged
Show file tree
Hide file tree
Changes from 50 commits
Commits
Show all changes
57 commits
Select commit Hold shift + click to select a range
a916d42
chore: remove unused internal association functions
juliannemarik Dec 19, 2023
499df3e
chore: enhance getTypeByIdsQuery util to accept multiple types
juliannemarik Dec 19, 2023
5ecd5d0
feat: add new getTypeByNotIdsQuery util
juliannemarik Dec 19, 2023
fc3df7a
chore: enhance getTypeWithKeyword util to accept multiple types
juliannemarik Dec 20, 2023
14d7974
chore: enhance getTypeWithoutKeyword util to accept multiple types
juliannemarik Dec 20, 2023
52fed1e
feat: add permissions for project/initiative association panes
juliannemarik Dec 20, 2023
0359335
feat: add association types and update IWithAssociations
juliannemarik Dec 20, 2023
5553414
feat: add group to updateHubEntity
juliannemarik Dec 20, 2023
368eac5
feat: add project and initiative association hierarchy definitions
juliannemarik Dec 20, 2023
b4c2273
chore: improve IWithAssociations comment
juliannemarik Dec 20, 2023
aacc0d8
feat: add internal getAssociationHierarchy util
juliannemarik Dec 20, 2023
3ecf4be
feat: add internal isAssociationSupported util
juliannemarik Dec 20, 2023
5821e7e
feat: add internal setAssociationKeyword util
juliannemarik Dec 20, 2023
58385c1
feat: add internal removeAssociationKeyword util
juliannemarik Dec 20, 2023
f4ecfde
feat: add internal getIdsFromAssociationGroups util
juliannemarik Dec 20, 2023
5e7ba68
feat: add internal getIdsFromKeywords util
juliannemarik Dec 20, 2023
5fd9db0
fix: code coverage
juliannemarik Dec 20, 2023
72dbae5
feat: add core getTypesFromEntityType util
juliannemarik Dec 21, 2023
9bccb79
feat: add internal getReferencesDoesNotIncludeQuery util
juliannemarik Dec 21, 2023
e0e5050
feat: add internal getIncludesDoesNotReferenceQuery util
juliannemarik Dec 21, 2023
69d049b
fix: code coverage
juliannemarik Dec 21, 2023
5890b51
fix: make getTypeWithKeywordQuery more flexible for future with multi…
juliannemarik Dec 21, 2023
045bd5c
feat: add internal getIncludesAndReferencesQuery util
juliannemarik Dec 21, 2023
062986b
chore: cleanup
juliannemarik Dec 21, 2023
f72f377
fix: tests
juliannemarik Dec 21, 2023
f905097
feat: add getAssociatedEntitiesQuery util
juliannemarik Dec 22, 2023
7a4ccc8
feat: add getPendingEntitiesQuery util
juliannemarik Dec 22, 2023
dd1302a
feat: add getRequestingEntitiesQuery util
juliannemarik Dec 22, 2023
7f4120f
fix: query doesn't include id predicate when no ids are provided
juliannemarik Dec 22, 2023
6d85ea5
chore: cleanup
juliannemarik Dec 22, 2023
3a0663c
fix: filter operation is optionally included if ids are provided
juliannemarik Dec 22, 2023
dd2f2ec
feat: add getAvailableToRequestEntitiesQuery util
juliannemarik Dec 22, 2023
abb6499
feat: abstract buildCatalog into internal util and add initiative col…
juliannemarik Dec 22, 2023
2af3903
feat: add getWellKnownAssociationsCatalog util
juliannemarik Dec 22, 2023
f11db25
feat: add requestAssociation util
juliannemarik Dec 22, 2023
c766298
feat: add acceptAssociation util
juliannemarik Dec 22, 2023
70dd105
chore: cleanup
juliannemarik Dec 22, 2023
cc1ca38
fix: test imports
juliannemarik Dec 22, 2023
3e39b95
chore: cleanup test import paths
juliannemarik Dec 27, 2023
e1042b4
chore: cleanup
juliannemarik Dec 27, 2023
1506902
feat: add breakAssociation util
juliannemarik Dec 27, 2023
0e16667
chore: add deprecated notice to addAssociation and removeAssociation
juliannemarik Dec 27, 2023
7592517
chore: add more deprecation notices
juliannemarik Dec 29, 2023
0ea680d
chore: remove existing association e2es for now
juliannemarik Dec 29, 2023
f7aac82
feat: add getAssociationStats util
juliannemarik Dec 29, 2023
571b915
chore: remove acceptAssociation and add as additional export
juliannemarik Jan 2, 2024
33b75b4
fix: test imports
juliannemarik Jan 2, 2024
ad342fa
feat: add associations to base property mapper
juliannemarik Jan 2, 2024
09da0a3
fix: handle empty state queries
juliannemarik Jan 2, 2024
7fa829a
fix: initiative project pane permissions
juliannemarik Jan 2, 2024
c3b6421
fix: wrap sharing/unsharing in try/catch
juliannemarik Jan 5, 2024
9bb072e
chore: move isSupported error throw to top of functions
juliannemarik Jan 5, 2024
be0d504
chore: export IAssociationStats interface and comment
juliannemarik Jan 5, 2024
aff067d
chore: add better description to getWellKnownAssociationsCatalog
juliannemarik Jan 5, 2024
3281d91
fix: getAssociationHierarchy test
juliannemarik Jan 5, 2024
4d01764
fix: test file name
juliannemarik Jan 8, 2024
df1e2f4
fix: typo
juliannemarik Jan 8, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
131 changes: 0 additions & 131 deletions packages/common/e2e/associations.e2e.ts

This file was deleted.

3 changes: 3 additions & 0 deletions packages/common/src/associations/addAssociation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@ import { IWithAssociations } from "../core/traits/IWithAssociations";
import { IAssociationInfo } from "./types";

/**
* ** DEPRECATED: please use requestAssociation instead.
* This will be removed in the next breaking version **
*
* Add an association to an entity
* Persisted into the entity's `.typeKeywords` array
* @param info
Expand Down
64 changes: 64 additions & 0 deletions packages/common/src/associations/breakAssociation.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
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) {
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);
await unshareItemWithGroup({
juliannemarik marked this conversation as resolved.
Show resolved Hide resolved
id,
groupId: associationGroupId,
authentication: context.session,
owner,
});
} else {
entity.typeKeywords = removeAssociationKeyword(
entity.typeKeywords,
associationType,
id
);
await updateHubEntity(entityType, entity, context);
}
} else {
juliannemarik marked this conversation as resolved.
Show resolved Hide resolved
throw new Error(
`breakAssociation: Association between ${entityType} and ${associationType} is not supported.`
);
}
};
52 changes: 52 additions & 0 deletions packages/common/src/associations/getAssociatedEntitiesQuery.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
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> => {
let query: IQuery;
const entityType = getTypeFromEntity(entity);
const isSupported = isAssociationSupported(entityType, associationType);

if (isSupported) {
const associationHierarchy = getAssociationHierarchy(entityType);
const isParent = associationHierarchy.children.includes(associationType);

query = await getIncludesAndReferencesQuery(
entity,
associationType,
isParent,
context
);
} else {
throw new Error(
juliannemarik marked this conversation as resolved.
Show resolved Hide resolved
`getAssociatedEntitiesQuery: Association between ${entityType} and ${associationType} is not supported.`
);
}

return query;
};
83 changes: 83 additions & 0 deletions packages/common/src/associations/getAssociationStats.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
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";

interface IAssociationStats {
juliannemarik marked this conversation as resolved.
Show resolved Hide resolved
associated: number;
juliannemarik marked this conversation as resolved.
Show resolved Hide resolved
pending: number;
requesting: number;
referenced?: number;
included?: number;
}

/**
* 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) {
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;
}
} else {
throw new Error(
`getAssociationStats: Association between ${entityType} and ${associationType} is not supported.`
);
}

return stats;
};
Loading