Skip to content

Commit

Permalink
H-3379, H-3408, H-3410: Entity editor: use closed schemas, allow draf…
Browse files Browse the repository at this point in the history
…t edits to entity's types (#5747)
  • Loading branch information
CiaranMn authored Nov 28, 2024
1 parent 6d935ac commit 4c7a2e4
Show file tree
Hide file tree
Showing 87 changed files with 3,185 additions and 1,708 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -542,6 +542,12 @@ export const convertSubgraphToSheetRequests = ({
const { sheetId, rowIndex } = entityPosition;
const linkedEntity = getEntityRevision(subgraph, leftEntityId);

if (!linkedEntity) {
throw new Error(
`Entity ${leftEntityId} not found in subgraph when processing link entity ${entity.metadata.recordId.entityId}`,
);
}

/** Create the link from this sheet to the source entity */
entityCells.push(
createHyperlinkCell({
Expand Down Expand Up @@ -599,6 +605,12 @@ export const convertSubgraphToSheetRequests = ({

const linkedEntity = getEntityRevision(subgraph, rightEntityId);

if (!linkedEntity) {
throw new Error(
`Entity ${rightEntityId} not found in subgraph when processing link entity ${entity.metadata.recordId.entityId}`,
);
}

entityCells.push(
createHyperlinkCell({
label: generateEntityLabel(subgraph, linkedEntity),
Expand Down
1 change: 0 additions & 1 deletion apps/hash-api/src/generate-ontology-type-ids.ts
Original file line number Diff line number Diff line change
Expand Up @@ -200,7 +200,6 @@ const generateOntologyIds = async () => {
},
},
graphApi,
temporalClient: null,
};

const [hashOrg, googleOrg, linearOrg] = await Promise.all([
Expand Down
18 changes: 9 additions & 9 deletions apps/hash-api/src/graph/context-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,25 +8,25 @@ import type { DataSource } from "apollo-datasource";
export type GraphApi = GraphApiClient & DataSource;

export type ImpureGraphContext<
WithUpload extends boolean = false,
WithTemporal extends boolean = false,
RequiresUpload extends boolean = false,
RequiresTemporal extends boolean = false,
> = {
graphApi: GraphApi;
provenance: EnforcedEntityEditionProvenance;
} & (WithUpload extends true
} & (RequiresUpload extends true
? { uploadProvider: UploadableStorageProvider }
: Record<string, unknown>) &
(WithTemporal extends true
: { uploadProvider?: UploadableStorageProvider }) &
(RequiresTemporal extends true
? { temporalClient: TemporalClient }
: Record<string, unknown>);
: { temporalClient?: TemporalClient });

export type ImpureGraphFunction<
Parameters,
ReturnType,
WithUpload extends boolean = false,
WithTemporal extends boolean = false,
RequiresUpload extends boolean = false,
RequiresTemporal extends boolean = false,
> = (
context: ImpureGraphContext<WithUpload, WithTemporal>,
context: ImpureGraphContext<RequiresUpload, RequiresTemporal>,
authentication: AuthenticationContext,
params: Parameters,
) => ReturnType;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ import isEqual from "lodash/isEqual";

import type { ImpureGraphContext } from "../../../context-types";
import {
getEntitySubgraph,
getEntitySubgraphResponse,
updateEntity,
} from "../../../knowledge/primitive/entity";
import { systemAccountId } from "../../../system-account";
Expand Down Expand Up @@ -62,44 +62,48 @@ export const upgradeWebEntities = async ({

const webBotAuthentication = { actorId: webBotAccountId };

const subgraph = await getEntitySubgraph(context, webBotAuthentication, {
filter: {
all: [
{
any: entityTypeBaseUrls.map((baseUrl) => ({
all: [
{
equal: [
{ path: ["type(inheritanceDepth = 0)", "baseUrl"] },
{ parameter: baseUrl },
],
},
const { subgraph } = await getEntitySubgraphResponse(
context,
webBotAuthentication,
{
filter: {
all: [
{
any: entityTypeBaseUrls.map((baseUrl) => ({
all: [
{
equal: [
{ path: ["type(inheritanceDepth = 0)", "baseUrl"] },
{ parameter: baseUrl },
],
},
{
less: [
{ path: ["type(inheritanceDepth = 0)", "version"] },
{ parameter: migrationState.entityTypeVersions[baseUrl] },
],
},
],
})),
},
{
equal: [
{ path: ["ownedById"] },
{
less: [
{ path: ["type(inheritanceDepth = 0)", "version"] },
{ parameter: migrationState.entityTypeVersions[baseUrl] },
],
parameter: webOwnedById,
},
],
})),
},
{
equal: [
{ path: ["ownedById"] },
{
parameter: webOwnedById,
},
],
},
],
},
],
},
graphResolveDepths: {
...zeroedGraphResolveDepths,
...fullOntologyResolveDepths,
},
includeDrafts: true,
temporalAxes: currentTimeInstantTemporalAxes,
},
graphResolveDepths: {
...zeroedGraphResolveDepths,
...fullOntologyResolveDepths,
},
includeDrafts: true,
temporalAxes: currentTimeInstantTemporalAxes,
});
);

const existingEntities = getRoots(subgraph);

Expand Down
111 changes: 69 additions & 42 deletions apps/hash-api/src/graph/knowledge/primitive/entity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ import {
import { systemEntityTypes } from "@local/hash-isomorphic-utils/ontology-type-ids";
import {
mapGraphApiEntityToEntity,
mapGraphApiEntityTypeResolveDefinitionsToEntityTypeResolveDefinitions,
mapGraphApiSubgraphToSubgraph,
} from "@local/hash-isomorphic-utils/subgraph-mapping";
import type {
Expand All @@ -59,6 +60,7 @@ import { ApolloError } from "apollo-server-errors";

import type {
EntityDefinition,
GetEntitySubgraphResponse,
LinkedEntityDefinition,
} from "../../../graphql/api-types.gen";
import { isTestEnv } from "../../../lib/env-config";
Expand Down Expand Up @@ -160,7 +162,7 @@ export const createEntity = async <Properties extends EntityProperties>(
export const getEntities: ImpureGraphFunction<
GetEntitiesRequest & { temporalClient?: TemporalClient },
Promise<Entity[]>
> = async ({ graphApi }, { actorId }, { temporalClient, ...params }) => {
> = async ({ graphApi, temporalClient }, { actorId }, params) => {
await rewriteSemanticFilter(params.filter, temporalClient);

const isRequesterAdmin = isTestEnv
Expand All @@ -185,12 +187,15 @@ export const getEntities: ImpureGraphFunction<
*
* @param params.query the structural query to filter entities by.
*/
export const getEntitySubgraph: ImpureGraphFunction<
GetEntitySubgraphRequest & {
temporalClient?: TemporalClient;
},
Promise<Subgraph<EntityRootType>>
> = async ({ graphApi }, { actorId }, { temporalClient, ...params }) => {
export const getEntitySubgraphResponse: ImpureGraphFunction<
GetEntitySubgraphRequest,
Promise<
Omit<
GetEntitySubgraphResponse,
"userPermissionsOnEntities" | "subgraph"
> & { subgraph: Subgraph<EntityRootType> }
>
> = async ({ graphApi, temporalClient }, { actorId }, params) => {
await rewriteSemanticFilter(params.filter, temporalClient);

const isRequesterAdmin = isTestEnv
Expand All @@ -202,8 +207,15 @@ export const getEntitySubgraph: ImpureGraphFunction<
);

return await graphApi.getEntitySubgraph(actorId, params).then(({ data }) => {
const {
subgraph: unfilteredSubgraph,
definitions,
closedMultiEntityTypes,
...rest
} = data;

const subgraph = mapGraphApiSubgraphToSubgraph<EntityRootType>(
data.subgraph,
unfilteredSubgraph,
actorId,
isRequesterAdmin,
);
Expand All @@ -223,7 +235,16 @@ export const getEntitySubgraph: ImpureGraphFunction<
}
}

return subgraph;
return {
closedMultiEntityTypes,
definitions: definitions
? mapGraphApiEntityTypeResolveDefinitionsToEntityTypeResolveDefinitions(
definitions,
)
: undefined,
subgraph,
...rest,
};
});
};

Expand All @@ -239,7 +260,7 @@ export const countEntities: ImpureGraphFunction<
* This function does NOT implement:
* 1. The ability to get the latest draft version without knowing its id.
* 2. The ability to get ALL versions of an entity at a given timestamp, i.e. if there is a live and one or more drafts
* – use {@link getEntitySubgraph} instead, includeDrafts, and match on its ownedById and uuid
* – use {@link getEntitySubgraphResponse} instead, includeDrafts, and match on its ownedById and uuid
*
* @param params.entityId the id of the entity, in one of the following formats:
* - `[webUuid]~[entityUuid]` for the 'live', non-draft version of the entity
Expand Down Expand Up @@ -750,39 +771,45 @@ export const getLatestEntityRootedSubgraph: ImpureGraphFunction<
> = async (context, authentication, params) => {
const { entity, graphResolveDepths } = params;

return await getEntitySubgraph(context, authentication, {
filter: {
all: [
{
equal: [
{ path: ["uuid"] },
{
parameter: extractEntityUuidFromEntityId(
entity.metadata.recordId.entityId,
),
},
],
},
{
equal: [
{ path: ["ownedById"] },
{
parameter: extractOwnedByIdFromEntityId(
entity.metadata.recordId.entityId,
),
},
],
},
{ equal: [{ path: ["archived"] }, { parameter: false }] },
],
},
graphResolveDepths: {
...zeroedGraphResolveDepths,
...graphResolveDepths,
const { subgraph } = await getEntitySubgraphResponse(
context,
authentication,
{
filter: {
all: [
{
equal: [
{ path: ["uuid"] },
{
parameter: extractEntityUuidFromEntityId(
entity.metadata.recordId.entityId,
),
},
],
},
{
equal: [
{ path: ["ownedById"] },
{
parameter: extractOwnedByIdFromEntityId(
entity.metadata.recordId.entityId,
),
},
],
},
{ equal: [{ path: ["archived"] }, { parameter: false }] },
],
},
graphResolveDepths: {
...zeroedGraphResolveDepths,
...graphResolveDepths,
},
temporalAxes: currentTimeInstantTemporalAxes,
includeDrafts: false,
},
temporalAxes: currentTimeInstantTemporalAxes,
includeDrafts: false,
});
);

return subgraph;
};

export const modifyEntityAuthorizationRelationships: ImpureGraphFunction<
Expand Down
Loading

0 comments on commit 4c7a2e4

Please sign in to comment.