Skip to content

Commit

Permalink
187692388 v3 DI Item Requests (#1284)
Browse files Browse the repository at this point in the history
* Handle delete, get, and update item requests.

* Handle delete, get, and update itemByID requests.

* Handle delete, get, and update itemByCaseID requests.
  • Loading branch information
tealefristoe authored Jun 1, 2024
1 parent 8369f78 commit d1abf66
Show file tree
Hide file tree
Showing 24 changed files with 647 additions and 139 deletions.
11 changes: 9 additions & 2 deletions v3/src/data-interactive/data-interactive-type-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ export function convertCaseToV2FullCase(c: ICase, dataContext: IDataSet) {
parent: parentCollectionInfo
} : undefined

const values = getCaseValues(caseId, collectionId, dataContext)
const values = getCaseValues(caseId, dataContext, collectionId)

return {
id: maybeToV2Id(caseGroup?.pseudoCase.__id__),
Expand All @@ -89,7 +89,7 @@ export function getCaseRequestResultValues(c: ICase, dataContext: IDataSet): DIG
name: _collection.name
} : undefined

const values = getCaseValues(caseId, collectionId, dataContext)
const values = getCaseValues(caseId, dataContext, collectionId)

const pseudoCase = dataContext.pseudoCaseMap.get(caseId)
const children = pseudoCase?.childPseudoCaseIds?.map(cId => toV2Id(cId)) ??
Expand Down Expand Up @@ -210,6 +210,13 @@ export function convertDataSetToV2(dataSet: IDataSet, docId: number | string): I
}
}

export function getV2ItemResult(dataContext: IDataSet, itemId: string) {
return {
id: toV2Id(itemId),
values: getCaseValues(itemId, dataContext)
}
}

export function basicDataSetInfo(dataSet: IDataSet) {
return {
name: dataSet.name,
Expand Down
35 changes: 29 additions & 6 deletions v3/src/data-interactive/data-interactive-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { RequireAtLeastOne } from "type-fest"
import { IAttribute } from "../models/data/attribute"
import { ICodapV2Attribute, ICodapV2AttributeV3, ICodapV2Collection, ICodapV2DataContext } from "../v2/codap-v2-types"
import { IDataSet } from "../models/data/data-set"
import { ICase } from "../models/data/data-set-types"
import { ICase, ICaseID } from "../models/data/data-set-types"
import { IGlobalValue } from "../models/global/global-value"
import { ITileModel } from "../models/tiles/tile-model"
import { ICollectionLabels, ICollectionPropsModel } from "../models/data/collection"
Expand Down Expand Up @@ -114,6 +114,11 @@ export interface DIUpdateCollection {
title?: string
labels?: Partial<ICollectionLabels>
}
export interface DIUpdateItemResult {
changedCases?: number[]
createdCases?: number[]
deletedCases?: number[]
}
export interface DIUpdateDataContext extends DIDataContext {
sort?: {
attr?: string
Expand All @@ -140,11 +145,11 @@ export interface DIResources {
global?: IGlobalValue
interactiveFrame?: ITileModel
isDefaultDataContext?: boolean
item?: DIItem
itemByCaseID?: DIItem
itemByID?: DIItem
item?: ICase
itemByCaseID?: ICase
itemByID?: ICase
itemCount?: number
itemSearch?: DIItem[]
itemSearch?: ICaseID[]
}

// types for values accepted as inputs by the API
Expand All @@ -156,7 +161,7 @@ export type DIValues = DISingleValues | DISingleValues[] | number | string[]
export type DIResultAttributes = { attrs: ICodapV2AttributeV3[] }
export type DIResultSingleValues = DICase | DIComponentInfo | DIGetCaseResult | DIGlobal | DIInteractiveFrame
export type DIResultValues = DIResultSingleValues | DIResultSingleValues[] |
DIAllCases | DIDeleteCollectionResult | DIResultAttributes | number
DIAllCases | DIDeleteCollectionResult | DIUpdateItemResult | DIResultAttributes | number | number[]

export interface DIMetadata {
dirtyDocument?: boolean
Expand Down Expand Up @@ -209,6 +214,10 @@ export interface DIResourceSelector {
dataContextList?: string
global?: string
interactiveFrame?: string
item?: string
itemByCaseID?: string
itemByID?: string
itemSearch?: string
logMessage?: string
type?: string
}
Expand All @@ -220,3 +229,17 @@ export interface DIAction {
}
export type DIRequest = DIAction | DIAction[]
export type DIRequestResponse = DIHandlerFnResult | DIHandlerFnResult[]

export type DIQueryValue = number | string | boolean
export type DIQueryFunction = (a?: DIQueryValue, b?: DIQueryValue) => boolean
export interface DIParsedOperand {
attr?: IAttribute
name: string
value: DIQueryValue
}
export interface DIParsedQuery {
valid: boolean
func: DIQueryFunction
left?: DIParsedOperand
right?: DIParsedOperand
}
7 changes: 4 additions & 3 deletions v3/src/data-interactive/data-interactive-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,10 @@ export function canonicalizeAttributeName(name: string, iCanonicalize = true) {
return tName
}

export function getCaseValues(caseId: string, collectionId: string, dataSet: IDataSet) {
const attributes = dataSet.getGroupedCollection(collectionId)?.attributes ??
dataSet.ungroupedAttributes
export function getCaseValues(caseId: string, dataSet: IDataSet, collectionId?: string) {
const attributes = collectionId
? dataSet.getGroupedCollection(collectionId)?.attributes ?? dataSet.ungroupedAttributes
: dataSet.attributes

const values: DICaseValues = {}
const actualCaseIndex = dataSet.caseIDMap.get(caseId) ?? -1
Expand Down
2 changes: 1 addition & 1 deletion v3/src/data-interactive/handlers/all-cases-handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ export const diAllCasesHandler: DIHandler = {
const childCaseIds = c.childCaseIds && Array.from(c.childCaseIds)
const children = childPseudoCaseIds ?? childCaseIds ?? []

const values = getCaseValues(id, collection.id, dataContext)
const values = getCaseValues(id, dataContext, collection.id)

return {
case: { id: toV2Id(id), parent, children: children.map(child => toV2Id(child)), values },
Expand Down
50 changes: 0 additions & 50 deletions v3/src/data-interactive/handlers/case-by-handler-functions.ts

This file was deleted.

4 changes: 2 additions & 2 deletions v3/src/data-interactive/handlers/case-by-id-handler.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { registerDIHandler } from "../data-interactive-handler"
import { DIHandler, DIResources, DIValues } from "../data-interactive-types"
import { deleteCaseBy, getCaseBy, updateCaseBy } from "./case-by-handler-functions"
import { deleteCaseBy, getCaseBy, updateCaseBy } from "./handler-functions"

export const diCaseByIDHandler: DIHandler = {
delete(resources: DIResources) {
Expand All @@ -10,7 +10,7 @@ export const diCaseByIDHandler: DIHandler = {
return getCaseBy(resources, resources.caseByID)
},
update(resources: DIResources, values?: DIValues) {
return updateCaseBy(resources, values, resources.caseByID)
return updateCaseBy(resources, values, resources.caseByID, { nestedValues: true, resourceName: "caseByID" })
}
}

Expand Down
4 changes: 2 additions & 2 deletions v3/src/data-interactive/handlers/case-by-index-handler.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { registerDIHandler } from "../data-interactive-handler"
import { DIHandler, DIResources, DIValues } from "../data-interactive-types"
import { deleteCaseBy, getCaseBy, updateCaseBy } from "./case-by-handler-functions"
import { deleteCaseBy, getCaseBy, updateCaseBy } from "./handler-functions"

export const diCaseByIndexHandler: DIHandler = {
delete(resources: DIResources) {
Expand All @@ -10,7 +10,7 @@ export const diCaseByIndexHandler: DIHandler = {
return getCaseBy(resources, resources.caseByIndex)
},
update(resources: DIResources, values?: DIValues) {
return updateCaseBy(resources, values, resources.caseByIndex)
return updateCaseBy(resources, values, resources.caseByIndex, { nestedValues: true, resourceName: "caseByIndex" })
}
}

Expand Down
22 changes: 2 additions & 20 deletions v3/src/data-interactive/handlers/case-handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { toV2Id, toV3CaseId } from "../../utilities/codap-utils"
import { registerDIHandler } from "../data-interactive-handler"
import { DIFullCase, DIHandler, DIResources, DIValues } from "../data-interactive-types"
import { attrNamesToIds } from "../data-interactive-utils"
import { updateCasesBy } from "./handler-functions"
import { dataContextNotFoundResult } from "./di-results"

export const diCaseHandler: DIHandler = {
Expand All @@ -29,26 +30,7 @@ export const diCaseHandler: DIHandler = {
return { success: true, values: itemIds.map(id => ({ itemID: toV2Id(id) })) }
},
update(resources: DIResources, values?: DIValues) {
const { dataContext } = resources
if (!dataContext) return dataContextNotFoundResult

const cases = (Array.isArray(values) ? values : [values]) as DIFullCase[]
const caseIDs: number[] = []
dataContext.applyModelChange(() => {
cases.forEach(aCase => {
const { id } = aCase
const v3Id = id && toV3CaseId(id)
if (v3Id && aCase.values && (dataContext.getCase(v3Id) || dataContext.pseudoCaseMap.get(v3Id))) {
caseIDs.push(id)
const updatedAttributes = attrNamesToIds(aCase.values, dataContext)
dataContext.setCaseValues([{ ...updatedAttributes, __id__: v3Id }])
}
})
})

if (caseIDs.length > 0) return { success: true, caseIDs }

return { success: false }
return updateCasesBy(resources, values)
}
}

Expand Down
2 changes: 2 additions & 0 deletions v3/src/data-interactive/handlers/di-results.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,7 @@ export const attributeNotFoundResult = errorResult(t("V3.DI.Error.attributeNotFo
export const caseNotFoundResult = errorResult(t("V3.DI.Error.caseNotFound"))
export const collectionNotFoundResult = errorResult(t("V3.DI.Error.collectionNotFound"))
export const componentNotFoundResult = errorResult(t("V3.DI.Error.componentNotFound"))
export const couldNotParseQueryResult = errorResult(t("V3.DI.Error.couldNotParseQuery"))
export const dataContextNotFoundResult = errorResult(t("V3.DI.Error.dataContextNotFound"))
export const itemNotFoundResult = errorResult(t("V3.DI.Error.itemNotFound"))
export const valuesRequiredResult = errorResult(t("V3.DI.Error.valuesRequired"))
127 changes: 127 additions & 0 deletions v3/src/data-interactive/handlers/handler-functions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
import { ICase } from "../../models/data/data-set-types"
import { toV2Id, toV3CaseId } from "../../utilities/codap-utils"
import { t } from "../../utilities/translation/translate"
import {
DICaseValues, DIFullCase, DIResources, DISuccessResult, DIUpdateCase, DIUpdateItemResult, DIValues
} from "../data-interactive-types"
import { getV2ItemResult, getCaseRequestResultValues } from "../data-interactive-type-utils"
import { attrNamesToIds } from "../data-interactive-utils"
import { caseNotFoundResult, dataContextNotFoundResult, itemNotFoundResult } from "./di-results"

export function deleteCaseBy(resources: DIResources, aCase?: ICase) {
const { dataContext } = resources
if (!dataContext) return dataContextNotFoundResult
if (!aCase) return caseNotFoundResult

const pseudoCase = dataContext.pseudoCaseMap.get(aCase.__id__)
const caseIds = pseudoCase?.childCaseIds ?? [aCase.__id__]

dataContext.applyModelChange(() => {
dataContext.removeCases(caseIds)
})

return { success: true as const, values: [toV2Id(aCase.__id__)] }
}

export function deleteItem(resources: DIResources, item?: ICase) {
const { dataContext } = resources
if (!dataContext) return dataContextNotFoundResult
if (!item) return itemNotFoundResult

dataContext.applyModelChange(() => {
dataContext.removeCases([item.__id__])
})

return { success: true as const, values: [toV2Id(item.__id__)] }
}

export function getCaseBy(resources: DIResources, aCase?: ICase) {
const { dataContext } = resources
if (!dataContext) return dataContextNotFoundResult
if (!aCase) return caseNotFoundResult

return { success: true as const, values: getCaseRequestResultValues(aCase, dataContext) }
}

export function getItem(resources: DIResources, item?: ICase) {
const { dataContext } = resources
if (!dataContext) return dataContextNotFoundResult
if (!item) return itemNotFoundResult

return { success: true as const, values: getV2ItemResult(dataContext, item.__id__) }
}

function itemResult({ changedCases, createdCases, deletedCases }: DIUpdateItemResult) {
return {
success: true,
values: {
changedCases: changedCases ?? [],
createdCases: createdCases ?? [],
deletedCases: deletedCases ?? []
}
} as DISuccessResult
}

export interface IUpdateCaseByOptions {
itemReturnStyle?: boolean
nestedValues?: boolean // Case requests expect values: { values: { ... } }
resourceName?: string
}
export function updateCaseBy(
resources: DIResources, values?: DIValues, aCase?: ICase, options?: IUpdateCaseByOptions
) {
const { dataContext } = resources
if (!dataContext) return dataContextNotFoundResult
if (!aCase) return caseNotFoundResult

const { itemReturnStyle, nestedValues, resourceName } = options ?? {}

const missingFieldResult = (field: string) => ({
success: false as const,
values: { error: t("V3.DI.Error.fieldRequired", { vars: ["update", resourceName ?? "case", field] }) }
})
if (!values) return missingFieldResult("values")

let _values = values as DICaseValues
if (nestedValues) {
const updateCase = values as DIUpdateCase
if (!updateCase.values) return missingFieldResult("values.values")
_values = updateCase.values
}

dataContext.applyModelChange(() => {
const updatedAttributes = attrNamesToIds(_values, dataContext)
dataContext.setCaseValues([{ ...updatedAttributes, __id__: aCase.__id__ }])
})

if (itemReturnStyle) return itemResult({ changedCases: [toV2Id(aCase.__id__)] })

return { success: true }
}

export function updateCasesBy(resources: DIResources, values?: DIValues, itemReturnStyle?: boolean) {
const { dataContext } = resources
if (!dataContext) return dataContextNotFoundResult

const cases = (Array.isArray(values) ? values : [values]) as DIFullCase[]
const caseIDs: number[] = []
dataContext.applyModelChange(() => {
cases.forEach(aCase => {
const { id } = aCase
const v3Id = id && toV3CaseId(id)
if (v3Id && aCase.values && (dataContext.getCase(v3Id) || dataContext.pseudoCaseMap.get(v3Id))) {
caseIDs.push(id)
const updatedAttributes = attrNamesToIds(aCase.values, dataContext)
dataContext.setCaseValues([{ ...updatedAttributes, __id__: v3Id }])
}
})
})

if (caseIDs.length > 0) {
if (itemReturnStyle) return itemResult({ changedCases: caseIDs })

return { success: true, caseIDs }
}

return { success: false }
}
Loading

0 comments on commit d1abf66

Please sign in to comment.