Mirror of https://github.com/roostorg/coop github.com/roostorg/coop
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

at 557ff54b2b435e5f1e789c6a8a4e1bebf2d7deb6 78 lines 2.6 kB view raw
1import _ from 'lodash'; 2 3const { omit } = _; 4 5/** 6 * In GraphQL, input types don't support unions in the same way output types do. 7 * Instead, for input types, the convention for representing what is logically 8 * still a tagged union is to use an object type where there's one field for 9 * each possible constituent of the union, and then only the applicable field is 10 * given a value on input data. This way of representing a union is inconsistent 11 * with how we tend to represent unions on the backend/in TS, which involves 12 * putting the discriminator as a `type` key's value within the union type's 13 * object value. 14 * 15 * Given these conventions, this function converts between the input type and 16 * our backend representations. 17 * 18 * E.g., if the schema is: 19 * 20 * ```gql 21 * input XInput { a: AInput, B: BInput } 22 * input AInput { hello: String! } 23 * input BInput { _: true } 24 * ``` 25 * 26 * Then calling: 27 * 28 * ``` 29 * oneOfInputToTaggedUnion( 30 * { a: { hello: 'World' } }, // value of union XInput 31 * { a: 'A', b: 'B' } // map of input keys to the final `type` values. 32 * ) 33 * ``` 34 * 35 * returns `{ type: 'A', hello: 'World' }` 36 * 37 * The use of an optional `_` property in the `BInput` type is a graphql 38 * convention for when one constituent of the union doesn't need any extra fields. 39 * (See https://github.com/graphql/graphql-spec/pull/825#issuecomment-1182979316). 40 * Accordingly, this function also strips off any input value field called `_`. 41 * 42 * This function uses `type` as the key for holding the tag, which we might 43 * support customizing later (though that gets tricky to express in TS). 44 * 45 * @param gqlInputValue 46 * @param inputKeyToTypeValueMap 47 * @returns 48 */ 49export function oneOfInputToTaggedUnion< 50 InputValue extends Record<string, object | undefined | null>, 51 TypeValue extends string, 52 Mapping extends { [K in keyof InputValue]: TypeValue }, 53>(gqlInputValue: InputValue, inputKeyToTypeValueMap: Mapping) { 54 const inputFilledEntries = Object.entries(gqlInputValue).filter( 55 ([_k, v]) => v != null, 56 ) as [keyof InputValue, InputValue][]; 57 58 if (inputFilledEntries.length !== 1) { 59 throw new Error( 60 'Input object must have exactly one key with a (non-null) value.', 61 ); 62 } 63 64 const [inputKey, inputValue] = inputFilledEntries[0]; 65 66 return { 67 type: inputKeyToTypeValueMap[inputKey], 68 ...omit(inputValue, '_'), 69 } as unknown as Exclude< 70 { 71 [K in keyof InputValue]: { type: Mapping[K] } & Omit< 72 Exclude<InputValue[K], null | undefined>, 73 '_' 74 >; 75 }[keyof InputValue], 76 undefined 77 >; 78}