Skip to content

Commit 49f187b

Browse files
authored
feat(datasets): add query by name and mutation to update dataset (#5595)
1 parent 0b8e01c commit 49f187b

File tree

5 files changed

+350
-31
lines changed

5 files changed

+350
-31
lines changed

apps/frontend/app/api/v1/osograph/schema.graphql

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,11 @@ type Query {
175175
"""
176176
osoApp_orgDatasets(orgName: String!): [Dataset!]!
177177

178+
"""
179+
Get a specific dataset by name
180+
"""
181+
osoApp_dataset(orgName: String!, datasetName: String!): Dataset
182+
178183
osoApp_datasetTableMetadata(
179184
orgName: String!
180185
catalogName: String!
@@ -240,6 +245,11 @@ type Mutation {
240245
Create a new dataset
241246
"""
242247
osoApp_createDataset(input: CreateDatasetInput!): CreateDatasetPayload!
248+
249+
"""
250+
Update a dataset
251+
"""
252+
osoApp_updateDataset(input: UpdateDatasetInput!): UpdateDatasetPayload!
243253
}
244254

245255
input CreateDatasetInput {
@@ -257,6 +267,20 @@ type CreateDatasetPayload {
257267
success: Boolean!
258268
}
259269

270+
input UpdateDatasetInput {
271+
datasetId: ID!
272+
name: String
273+
displayName: String
274+
description: String
275+
isPublic: Boolean
276+
}
277+
278+
type UpdateDatasetPayload {
279+
dataset: Dataset
280+
message: String!
281+
success: Boolean!
282+
}
283+
260284
input CreateInvitationInput {
261285
"""
262286
Email address to invite

apps/frontend/app/api/v1/osograph/schema/resolvers/dataset.ts

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import {
66
type GraphQLContext,
77
} from "@/app/api/v1/osograph/utils/auth";
88
import {
9+
AuthenticationErrors,
910
ResourceErrors,
1011
ServerErrors,
1112
} from "@/app/api/v1/osograph/utils/errors";
@@ -153,6 +154,34 @@ export const datasetResolver: GraphQLResolverModule<GraphQLContext> = {
153154
return response;
154155
},
155156

157+
osoApp_dataset: async (
158+
_: unknown,
159+
{ orgName, datasetName }: { orgName: string; datasetName: string },
160+
context: GraphQLContext,
161+
) => {
162+
const authenticatedUser = requireAuthentication(context.user);
163+
const organization = await getOrganizationByName(orgName);
164+
await requireOrgMembership(authenticatedUser.userId, organization.id);
165+
166+
const supabase = createAdminClient();
167+
const { data, error } = await supabase
168+
.from("datasets")
169+
.select("*, models:model(*)")
170+
.eq("org_id", organization.id)
171+
.eq("name", datasetName)
172+
.is("deleted_at", null)
173+
.single();
174+
175+
if (error) {
176+
throw ResourceErrors.notFound("Dataset", `name: ${datasetName}`);
177+
}
178+
179+
return {
180+
...data,
181+
tables: data.models.map((m) => ({ name: m.name })),
182+
};
183+
},
184+
156185
osoApp_datasetTableMetadata: async (
157186
_: unknown,
158187
{
@@ -273,6 +302,63 @@ export const datasetResolver: GraphQLResolverModule<GraphQLContext> = {
273302
success: true,
274303
};
275304
},
305+
osoApp_updateDataset: async (
306+
_: unknown,
307+
{
308+
datasetId,
309+
name,
310+
displayName,
311+
description,
312+
isPublic,
313+
}: {
314+
datasetId: string;
315+
name?: string;
316+
displayName?: string;
317+
description?: string;
318+
isPublic?: boolean;
319+
},
320+
context: GraphQLContext,
321+
) => {
322+
const authenticatedUser = requireAuthentication(context.user);
323+
const supabase = createAdminClient();
324+
325+
const { data: existingDataset, error: existingError } = await supabase
326+
.from("datasets")
327+
.select("org_id")
328+
.eq("id", datasetId)
329+
.single();
330+
331+
if (existingError || !existingDataset) {
332+
throw AuthenticationErrors.notAuthorized();
333+
}
334+
335+
await requireOrgMembership(
336+
authenticatedUser.userId,
337+
existingDataset.org_id,
338+
);
339+
340+
const { data, error } = await supabase
341+
.from("datasets")
342+
.update({
343+
name,
344+
display_name: displayName,
345+
description,
346+
is_public: isPublic,
347+
})
348+
.eq("id", datasetId)
349+
.select()
350+
.single();
351+
352+
if (error) {
353+
throw ServerErrors.database("Failed to update dataset");
354+
}
355+
356+
return {
357+
dataset: data,
358+
message: "Dataset updated successfully",
359+
success: true,
360+
};
361+
},
276362
},
277363
Dataset: {
278364
id: (parent: DatasetsRow) => parent.id,

apps/frontend/lib/clients/oso-app/oso-app.ts

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2519,6 +2519,77 @@ class OsoAppClient {
25192519

25202520
return payload.dataset;
25212521
}
2522+
2523+
async updateDataset(
2524+
args: Partial<{
2525+
datasetId: string;
2526+
name: string;
2527+
displayName: string;
2528+
description: string;
2529+
isPublic: boolean;
2530+
}>,
2531+
) {
2532+
const { datasetId, name, displayName, description, isPublic } = {
2533+
datasetId: ensure(args.datasetId, "Missing datasetId argument"),
2534+
name: args.name,
2535+
displayName: args.displayName,
2536+
description: args.description,
2537+
isPublic: args.isPublic,
2538+
};
2539+
2540+
const UPDATE_DATASET_MUTATION = gql(`
2541+
mutation UpdateDataset($input: UpdateDatasetInput!) {
2542+
osoApp_updateDataset(input: $input) {
2543+
success
2544+
message
2545+
dataset {
2546+
id
2547+
name
2548+
displayName
2549+
description
2550+
isPublic
2551+
}
2552+
}
2553+
}
2554+
`);
2555+
2556+
const response = await fetch("/api/v1/osograph", {
2557+
method: "POST",
2558+
headers: {
2559+
"Content-Type": "application/json",
2560+
},
2561+
body: JSON.stringify({
2562+
query: print(UPDATE_DATASET_MUTATION),
2563+
variables: {
2564+
input: {
2565+
datasetId,
2566+
name,
2567+
displayName,
2568+
description,
2569+
isPublic,
2570+
},
2571+
},
2572+
}),
2573+
});
2574+
2575+
const result = await response.json();
2576+
2577+
if (result.errors) {
2578+
logger.error("Failed to update dataset:", result.errors[0].message);
2579+
throw new Error(`Failed to update dataset: ${result.errors[0].message}`);
2580+
}
2581+
2582+
const payload = result.data?.osoApp_updateDataset;
2583+
if (!payload) {
2584+
throw new Error("No response data from update dataset mutation");
2585+
}
2586+
2587+
if (payload.success) {
2588+
logger.log(`Successfully updated dataset "${displayName}"`);
2589+
}
2590+
2591+
return payload.dataset;
2592+
}
25222593
}
25232594

25242595
export { OsoAppClient };

apps/frontend/lib/graphql/generated/gql.ts

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,19 +14,22 @@ import { TypedDocumentNode as DocumentNode } from "@graphql-typed-document-node/
1414
* Learn more about it here: https://the-guild.dev/graphql/codegen/plugins/presets/preset-client#reducing-bundle-size
1515
*/
1616
type Documents = {
17-
"\n mutation CreateDataset($input: CreateDatasetInput!) {\n osoApp_createDataset(input: $input) {\n success\n message\n dataset {\n id\n name\n displayName\n description\n catalog\n schema\n datasetType\n isPublic\n }\n }\n }\n ": typeof types.CreateDatasetDocument;
17+
"\n mutation UpdateDataset($input: UpdateDatasetInput!) {\n osoApp_updateDataset(input: $input) {\n success\n message\n dataset {\n id\n name\n displayName\n description\n isPublic\n }\n }\n }\n ": typeof types.UpdateDatasetDocument;
1818
"\n mutation SavePreview($input: SaveNotebookPreviewInput!) {\n osoApp_saveNotebookPreview(input: $input) {\n success\n message\n }\n }\n ": typeof types.SavePreviewDocument;
19+
"\n mutation CreateDataset($input: CreateDatasetInput!) {\n osoApp_createDataset(input: $input) {\n success\n message\n dataset {\n id\n name\n displayName\n description\n catalog\n schema\n datasetType\n isPublic\n }\n }\n }\n ": typeof types.CreateDatasetDocument;
1920
"\nquery AssetGraph {\n assetNodes {\n assetKey {\n path\n }\n dependencyKeys {\n path\n }\n }\n}": typeof types.AssetGraphDocument;
2021
'\nquery AssetMaterializedData($assetKeys: [AssetKeyInput!] = {path: ""}) {\n assetNodes(assetKeys: $assetKeys) {\n assetKey {\n path\n }\n partitionStats {\n numFailed\n numMaterialized\n numMaterializing\n numPartitions\n }\n assetPartitionStatuses {\n ... on TimePartitionStatuses {\n __typename\n ranges {\n endKey\n startKey\n status\n }\n }\n }\n assetMaterializations(limit: 1) {\n runOrError {\n ... on Run {\n endTime\n }\n }\n }\n }\n}': typeof types.AssetMaterializedDataDocument;
2122
"\n query TimeseriesMetricsByArtifact(\n $artifactIds: [String!],\n $metricIds: [String!],\n $startDate: Oso_Date!,\n $endDate: Oso_Date!, \n ) {\n oso_timeseriesMetricsByArtifactV0(where: {\n artifactId: {_in: $artifactIds},\n metricId: {_in: $metricIds},\n sampleDate: { _gte: $startDate, _lte: $endDate }\n }) {\n amount\n artifactId\n metricId\n sampleDate\n unit\n }\n oso_artifactsV1(where: { artifactId: { _in: $artifactIds }}) {\n artifactId\n artifactSource\n artifactNamespace\n artifactName\n }\n oso_metricsV0(where: {metricId: {_in: $metricIds}}) {\n metricId\n metricSource\n metricNamespace\n metricName\n displayName\n description\n }\n }\n": typeof types.TimeseriesMetricsByArtifactDocument;
2223
"\n query TimeseriesMetricsByProject(\n $projectIds: [String!],\n $metricIds: [String!],\n $startDate: Oso_Date!,\n $endDate: Oso_Date!, \n ) {\n oso_timeseriesMetricsByProjectV0(where: {\n projectId: {_in: $projectIds},\n metricId: {_in: $metricIds},\n sampleDate: { _gte: $startDate, _lte: $endDate }\n }) {\n amount\n metricId\n projectId\n sampleDate\n unit\n }\n oso_projectsV1(where: { projectId: { _in: $projectIds }}) {\n projectId\n projectSource\n projectNamespace\n projectName\n displayName\n description\n }\n oso_metricsV0(where: {metricId: {_in: $metricIds}}) {\n metricId\n metricSource\n metricNamespace\n metricName\n displayName\n description\n }\n }\n": typeof types.TimeseriesMetricsByProjectDocument;
2324
"\n query TimeseriesMetricsByCollection(\n $collectionIds: [String!],\n $metricIds: [String!],\n $startDate: Oso_Date!,\n $endDate: Oso_Date!, \n ) {\n oso_timeseriesMetricsByCollectionV0(where: {\n collectionId: {_in: $collectionIds},\n metricId: {_in: $metricIds},\n sampleDate: { _gte: $startDate, _lte: $endDate }\n }) {\n amount\n metricId\n collectionId\n sampleDate\n unit\n }\n oso_collectionsV1(where: { collectionId: { _in: $collectionIds }}) {\n collectionId\n collectionSource\n collectionNamespace\n collectionName\n displayName\n description\n }\n oso_metricsV0(where: {metricId: {_in: $metricIds}}) {\n metricId\n metricSource\n metricNamespace\n metricName\n displayName\n description\n }\n }\n": typeof types.TimeseriesMetricsByCollectionDocument;
2425
};
2526
const documents: Documents = {
26-
"\n mutation CreateDataset($input: CreateDatasetInput!) {\n osoApp_createDataset(input: $input) {\n success\n message\n dataset {\n id\n name\n displayName\n description\n catalog\n schema\n datasetType\n isPublic\n }\n }\n }\n ":
27-
types.CreateDatasetDocument,
27+
"\n mutation UpdateDataset($input: UpdateDatasetInput!) {\n osoApp_updateDataset(input: $input) {\n success\n message\n dataset {\n id\n name\n displayName\n description\n isPublic\n }\n }\n }\n ":
28+
types.UpdateDatasetDocument,
2829
"\n mutation SavePreview($input: SaveNotebookPreviewInput!) {\n osoApp_saveNotebookPreview(input: $input) {\n success\n message\n }\n }\n ":
2930
types.SavePreviewDocument,
31+
"\n mutation CreateDataset($input: CreateDatasetInput!) {\n osoApp_createDataset(input: $input) {\n success\n message\n dataset {\n id\n name\n displayName\n description\n catalog\n schema\n datasetType\n isPublic\n }\n }\n }\n ":
32+
types.CreateDatasetDocument,
3033
"\nquery AssetGraph {\n assetNodes {\n assetKey {\n path\n }\n dependencyKeys {\n path\n }\n }\n}":
3134
types.AssetGraphDocument,
3235
'\nquery AssetMaterializedData($assetKeys: [AssetKeyInput!] = {path: ""}) {\n assetNodes(assetKeys: $assetKeys) {\n assetKey {\n path\n }\n partitionStats {\n numFailed\n numMaterialized\n numMaterializing\n numPartitions\n }\n assetPartitionStatuses {\n ... on TimePartitionStatuses {\n __typename\n ranges {\n endKey\n startKey\n status\n }\n }\n }\n assetMaterializations(limit: 1) {\n runOrError {\n ... on Run {\n endTime\n }\n }\n }\n }\n}':
@@ -57,14 +60,20 @@ export function gql(source: string): unknown;
5760
* The gql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
5861
*/
5962
export function gql(
60-
source: "\n mutation CreateDataset($input: CreateDatasetInput!) {\n osoApp_createDataset(input: $input) {\n success\n message\n dataset {\n id\n name\n displayName\n description\n catalog\n schema\n datasetType\n isPublic\n }\n }\n }\n ",
61-
): (typeof documents)["\n mutation CreateDataset($input: CreateDatasetInput!) {\n osoApp_createDataset(input: $input) {\n success\n message\n dataset {\n id\n name\n displayName\n description\n catalog\n schema\n datasetType\n isPublic\n }\n }\n }\n "];
63+
source: "\n mutation UpdateDataset($input: UpdateDatasetInput!) {\n osoApp_updateDataset(input: $input) {\n success\n message\n dataset {\n id\n name\n displayName\n description\n isPublic\n }\n }\n }\n ",
64+
): (typeof documents)["\n mutation UpdateDataset($input: UpdateDatasetInput!) {\n osoApp_updateDataset(input: $input) {\n success\n message\n dataset {\n id\n name\n displayName\n description\n isPublic\n }\n }\n }\n "];
6265
/**
6366
* The gql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
6467
*/
6568
export function gql(
6669
source: "\n mutation SavePreview($input: SaveNotebookPreviewInput!) {\n osoApp_saveNotebookPreview(input: $input) {\n success\n message\n }\n }\n ",
6770
): (typeof documents)["\n mutation SavePreview($input: SaveNotebookPreviewInput!) {\n osoApp_saveNotebookPreview(input: $input) {\n success\n message\n }\n }\n "];
71+
/**
72+
* The gql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
73+
*/
74+
export function gql(
75+
source: "\n mutation CreateDataset($input: CreateDatasetInput!) {\n osoApp_createDataset(input: $input) {\n success\n message\n dataset {\n id\n name\n displayName\n description\n catalog\n schema\n datasetType\n isPublic\n }\n }\n }\n ",
76+
): (typeof documents)["\n mutation CreateDataset($input: CreateDatasetInput!) {\n osoApp_createDataset(input: $input) {\n success\n message\n dataset {\n id\n name\n displayName\n description\n catalog\n schema\n datasetType\n isPublic\n }\n }\n }\n "];
6877
/**
6978
* The gql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
7079
*/

0 commit comments

Comments
 (0)