Skip to content

Commit

Permalink
H-4033: Data type editor MVP (#6484)
Browse files Browse the repository at this point in the history
  • Loading branch information
CiaranMn authored Feb 20, 2025
1 parent 855882a commit 525e1d9
Show file tree
Hide file tree
Showing 79 changed files with 4,488 additions and 476 deletions.
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { fullTransactionTimeAxis } from "@local/hash-isomorphic-utils/graph-queries";
import {
descriptionPropertyTypeUrl,
fileUrlPropertyTypeUrl,
Expand All @@ -13,6 +14,18 @@ import {
createSystemPropertyTypeIfNotExists,
} from "../util";

/**
* @todo H-4065 / H-4066: Support array and tuple data types (add /list/ here, which they should inherit from)
*/
const blockProtocolDataTypeIds = [
"https://blockprotocol.org/@blockprotocol/types/data-type/boolean/v/1",
"https://blockprotocol.org/@blockprotocol/types/data-type/null/v/1",
"https://blockprotocol.org/@blockprotocol/types/data-type/number/v/1",
"https://blockprotocol.org/@blockprotocol/types/data-type/object/v/1",
"https://blockprotocol.org/@blockprotocol/types/data-type/text/v/1",
"https://blockprotocol.org/@blockprotocol/types/data-type/value/v/1",
] as const;

const migrate: MigrationFunction = async ({
context,
authentication,
Expand Down Expand Up @@ -1651,6 +1664,31 @@ const migrate: MigrationFunction = async ({
instantiator: anyUserInstantiator,
});

/**
* Ensure the primitive BP data types are loaded
*/
await Promise.all(
blockProtocolDataTypeIds.map(async (dataTypeId) => {
const existingDataType = await context.graphApi
.getDataTypes(authentication.actorId, {
filter: {
equal: [{ path: ["versionedUrl"] }, { parameter: dataTypeId }],
},
includeDrafts: false,
temporalAxes: fullTransactionTimeAxis,
})
.then((response) => response.data.dataTypes);

if (existingDataType.length > 0) {
return;
}

return context.graphApi.loadExternalDataType(authentication.actorId, {
dataTypeId,
});
}),
);

return migrationState;
};

Expand Down
24 changes: 24 additions & 0 deletions apps/hash-api/src/graph/ontology/primitive/data-type.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import type {
} from "@blockprotocol/type-system";
import { DATA_TYPE_META_SCHEMA } from "@blockprotocol/type-system";
import { NotFoundError } from "@local/hash-backend-utils/error";
import { publicUserAccountId } from "@local/hash-backend-utils/public-user-account-id";
import type {
ArchiveDataTypeParams,
DataTypePermission,
Expand All @@ -30,6 +31,7 @@ import {
mapGraphApiDataTypesToDataTypes,
mapGraphApiSubgraphToSubgraph,
} from "@local/hash-isomorphic-utils/subgraph-mapping";
import type { UserPermissionsOnDataType } from "@local/hash-isomorphic-utils/types";
import type {
DataTypeAuthorizationRelationship,
DataTypeRelationAndSubject,
Expand Down Expand Up @@ -330,3 +332,25 @@ export const checkDataTypePermission: ImpureGraphFunction<
graphApi
.checkDataTypePermission(actorId, params.dataTypeId, params.permission)
.then(({ data }) => data.has_permission);

export const checkPermissionsOnDataType: ImpureGraphFunction<
{ dataTypeId: VersionedUrl },
Promise<UserPermissionsOnDataType>
> = async (graphContext, { actorId }, params) => {
const { dataTypeId } = params;

const isPublicUser = actorId === publicUserAccountId;

const canUpdate = isPublicUser
? false
: await checkDataTypePermission(
graphContext,
{ actorId },
{ dataTypeId, permission: "update" },
);

return {
edit: canUpdate,
view: true,
};
};
10 changes: 10 additions & 0 deletions apps/hash-api/src/graphql/resolvers/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -81,9 +81,14 @@ import { submitEarlyAccessFormResolver } from "./knowledge/user/submit-early-acc
import { loggedInMiddleware } from "./middlewares/logged-in";
import { loggedInAndSignedUpMiddleware } from "./middlewares/logged-in-and-signed-up";
import {
archiveDataTypeResolver,
checkUserPermissionsOnDataTypeResolver,
createDataTypeResolver,
getDataType,
getDataTypeConversionTargetsResolver,
queryDataTypes,
unarchiveDataTypeResolver,
updateDataTypeResolver,
} from "./ontology/data-type";
import {
archiveEntityTypeResolver,
Expand Down Expand Up @@ -151,6 +156,7 @@ export const resolvers: Omit<Resolvers, "Query" | "Mutation"> & {
checkUserPermissionsOnEntity: (_, { metadata }, context, info) =>
checkUserPermissionsOnEntity({ metadata }, _, context, info),
checkUserPermissionsOnEntityType: checkUserPermissionsOnEntityTypeResolver,
checkUserPermissionsOnDataType: checkUserPermissionsOnDataTypeResolver,
hasAccessToHash: loggedInMiddleware(hasAccessToHashResolver),
// Generation
generateInverse: loggedInMiddleware(generateInverseResolver),
Expand Down Expand Up @@ -179,6 +185,10 @@ export const resolvers: Omit<Resolvers, "Query" | "Mutation"> & {
unarchivePropertyType: loggedInAndSignedUpMiddleware(
unarchivePropertyTypeResolver,
),
createDataType: loggedInAndSignedUpMiddleware(createDataTypeResolver),
updateDataType: loggedInAndSignedUpMiddleware(updateDataTypeResolver),
archiveDataType: loggedInAndSignedUpMiddleware(archiveDataTypeResolver),
unarchiveDataType: loggedInAndSignedUpMiddleware(unarchiveDataTypeResolver),
createEntityType: loggedInAndSignedUpMiddleware(createEntityTypeResolver),
updateEntityType: loggedInAndSignedUpMiddleware(updateEntityTypeResolver),
updateEntityTypes: loggedInAndSignedUpMiddleware(updateEntityTypesResolver),
Expand Down
107 changes: 103 additions & 4 deletions apps/hash-api/src/graphql/resolvers/ontology/data-type.ts
Original file line number Diff line number Diff line change
@@ -1,23 +1,37 @@
import type { DataTypeWithMetadata } from "@blockprotocol/graph";
import type { OntologyTemporalMetadata } from "@local/hash-graph-client/dist/api.d";
import type { DataTypeConversionsMap } from "@local/hash-isomorphic-utils/data-types";
import {
currentTimeInstantTemporalAxes,
defaultDataTypeAuthorizationRelationships,
fullTransactionTimeAxis,
zeroedGraphResolveDepths,
} from "@local/hash-isomorphic-utils/graph-queries";
import {
mapGraphApiSubgraphToSubgraph,
serializeSubgraph,
} from "@local/hash-isomorphic-utils/subgraph-mapping";
import type { UserPermissionsOnDataType } from "@local/hash-isomorphic-utils/types";
import type {
DataTypeRootType,
SerializedSubgraph,
} from "@local/hash-subgraph";

import {
archiveDataType,
checkPermissionsOnDataType,
createDataType,
getDataTypeConversionTargets,
getDataTypeSubgraphById,
unarchiveDataType,
updateDataType,
} from "../../../graph/ontology/primitive/data-type";
import type {
MutationArchiveDataTypeArgs,
MutationCreateDataTypeArgs,
MutationUnarchiveDataTypeArgs,
MutationUpdateDataTypeArgs,
QueryCheckUserPermissionsOnDataTypeArgs,
QueryGetDataTypeArgs,
QueryGetDataTypeConversionTargetsArgs,
QueryQueryDataTypesArgs,
Expand All @@ -33,19 +47,26 @@ export const queryDataTypes: ResolverFn<
QueryQueryDataTypesArgs
> = async (
_,
{ constrainsValuesOn, includeArchived },
{ constrainsValuesOn, filter, includeArchived, inheritsFrom, latestOnly },
{ dataSources, authentication },
) => {
const { graphApi } = dataSources;

const latestOnlyFilter = {
equal: [{ path: ["version"] }, { parameter: "latest" }],
};

const { data: response } = await graphApi.getDataTypeSubgraph(
authentication.actorId,
{
filter: {
equal: [{ path: ["version"] }, { parameter: "latest" }],
},
filter: latestOnly
? filter
? { all: [filter, latestOnlyFilter] }
: latestOnlyFilter
: (filter ?? { all: [] }),
graphResolveDepths: {
...zeroedGraphResolveDepths,
inheritsFrom,
constrainsValuesOn,
},
temporalAxes: includeArchived
Expand Down Expand Up @@ -105,3 +126,81 @@ export const getDataTypeConversionTargetsResolver: ResolverFn<
{ dataTypeIds },
);
};

export const createDataTypeResolver: ResolverFn<
Promise<DataTypeWithMetadata>,
Record<string, never>,
LoggedInGraphQLContext,
MutationCreateDataTypeArgs
> = async (_, params, { dataSources, authentication, provenance }) => {
const { ownedById, dataType } = params;

const createdDataType = await createDataType(
{
...dataSources,
provenance,
},
authentication,
{
ownedById,
schema: dataType,
relationships: defaultDataTypeAuthorizationRelationships,
conversions: {},
},
);

return createdDataType;
};

export const updateDataTypeResolver: ResolverFn<
Promise<DataTypeWithMetadata>,
Record<string, never>,
LoggedInGraphQLContext,
MutationUpdateDataTypeArgs
> = async (_, params, graphQLContext) =>
updateDataType(
graphQLContextToImpureGraphContext(graphQLContext),
graphQLContext.authentication,
{
dataTypeId: params.dataTypeId,
schema: params.dataType,
relationships: defaultDataTypeAuthorizationRelationships,
conversions: {},
},
);

export const archiveDataTypeResolver: ResolverFn<
Promise<OntologyTemporalMetadata>,
Record<string, never>,
LoggedInGraphQLContext,
MutationArchiveDataTypeArgs
> = async (_, params, graphQLContext) =>
archiveDataType(
graphQLContextToImpureGraphContext(graphQLContext),
graphQLContext.authentication,
params,
);

export const unarchiveDataTypeResolver: ResolverFn<
Promise<OntologyTemporalMetadata>,
Record<string, never>,
LoggedInGraphQLContext,
MutationUnarchiveDataTypeArgs
> = async (_, params, graphQLContext) =>
unarchiveDataType(
graphQLContextToImpureGraphContext(graphQLContext),
graphQLContext.authentication,
params,
);

export const checkUserPermissionsOnDataTypeResolver: ResolverFn<
Promise<UserPermissionsOnDataType>,
Record<string, never>,
LoggedInGraphQLContext,
QueryCheckUserPermissionsOnDataTypeArgs
> = async (_, params, { dataSources, authentication, provenance }) =>
checkPermissionsOnDataType(
{ ...dataSources, provenance },
authentication,
params,
);
22 changes: 1 addition & 21 deletions apps/hash-api/src/graphql/resolvers/ontology/entity-type.ts
Original file line number Diff line number Diff line change
Expand Up @@ -196,27 +196,7 @@ export const updateEntityTypeResolver: ResolverFn<
{
entityTypeId: params.entityTypeId,
schema: params.updatedEntityType,
relationships: [
{
relation: "setting",
subject: {
kind: "setting",
subjectId: "updateFromWeb",
},
},
{
relation: "viewer",
subject: {
kind: "public",
},
},
{
relation: "instantiator",
subject: {
kind: "public",
},
},
],
relationships: defaultEntityTypeAuthorizationRelationships,
},
);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ export const useBlockProtocolQueryDataTypes = (): {
const response = await queryFn({
variables: {
constrainsValuesOn: { outgoing: 255 },
inheritsFrom: { outgoing: 255 },
latestOnly: true,
},
});

Expand Down
Original file line number Diff line number Diff line change
@@ -1,42 +1,42 @@
import type { AccountId } from "@local/hash-graph-types/account";
import type { AccountEntityId } from "@local/hash-subgraph";
import { extractAccountId } from "@local/hash-subgraph";
import type { OwnedById } from "@local/hash-graph-types/web";
import { extractOwnedByIdFromEntityId } from "@local/hash-subgraph";
import { useMemo } from "react";

import { useOrgs } from "./use-orgs";
import { useUsers } from "./use-users";

export const useGetAccountIdForShortname = (
export const useGetOwnedByIdForShortname = (
shortname: string | undefined,
): { loading: boolean; accountId: AccountId | undefined } => {
): { loading: boolean; ownedById: OwnedById | undefined } => {
const { loading: usersLoading, users } = useUsers();
const { loading: orgsLoading, orgs } = useOrgs();

const accountId = useMemo(() => {
const ownedById = useMemo(() => {
/** @todo - don't do extract anymore */
const userBaseId = users?.find((user) => user.shortname === shortname)
const userEntityId = users?.find((user) => user.shortname === shortname)
?.entity.metadata.recordId.entityId;
const userAccountId = userBaseId
? extractAccountId(userBaseId as AccountEntityId)

const userOwnedById = userEntityId
? extractOwnedByIdFromEntityId(userEntityId)
: undefined;

if (userAccountId !== undefined) {
return userAccountId;
if (userOwnedById !== undefined) {
return userOwnedById;
}

const orgBaseId = orgs?.find((org) => org.shortname === shortname)?.entity
const orgEntityId = orgs?.find((org) => org.shortname === shortname)?.entity
.metadata.recordId.entityId;
const orgAccountId = orgBaseId
? extractAccountId(orgBaseId as AccountEntityId)
const orgOwnedById = orgEntityId
? extractOwnedByIdFromEntityId(orgEntityId)
: undefined;

if (orgAccountId !== undefined) {
return orgAccountId;
if (orgOwnedById !== undefined) {
return orgOwnedById;
}
}, [users, orgs, shortname]);

return {
loading: usersLoading || orgsLoading,
accountId,
ownedById,
};
};
Loading

0 comments on commit 525e1d9

Please sign in to comment.