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 3a75984654db888a95d657c181e4e2a1c3a46b2d 86 lines 3.1 kB view raw
1import isPlainObject from 'lodash/isPlainObject'; 2import mapValues from 'lodash/mapValues'; 3import omit from 'lodash/omit'; 4 5/** 6 * In GraphQL, it's often not possible to make output types and input types 7 * totally symmetric, because output types support unions of object types (where 8 * the __typename key is a discriminator) but input types don't. Instead, for 9 * input types, the convention is to use an object type where there's one field 10 * for each possible constituent of the logical input type union, and then only 11 * the applicable field is set on input. Given these conventions, this function 12 * converts between the output type result and the input type. 13 * 14 * E.g., if the schema is: 15 * 16 * ``` 17 * union X = A | B 18 * type A { hello: String! } 19 * type B { goodbye: Boolean! } 20 * 21 * input XInput { a: AInput, B: BInput } 22 * input AInput { hello: String! } 23 * input BInput { goodbye: BOolean! } 24 * ``` 25 * 26 * Then calling: 27 * 28 * ``` 29 * taggedUnionToOneOfInput( 30 * { __typename: 'A', hello: 'World' }, // value of union X, tagged by __typename. 31 * { A: 'a', B: 'b' } // map of the tag values ('A', and 'B') to the input keys. 32 * ) 33 * ``` 34 * 35 * returns `{ a: { hello: 'World' } }` 36 * 37 * This function looks for the tag key in either `__typename` (which will be the 38 * case w/ GraphQL output unions) or `type` (which some of our old output types 39 * used because they were mirroring typescript). 40 * 41 * @param taggedUnionValue 42 * @param tagValueToInputKeyMap 43 * @returns 44 */ 45export function taggedUnionToOneOfInput<U extends string>( 46 taggedUnionValue: ({ type: U } | { __typename: U }) & { [k: string]: any }, 47 tagValueToInputKeyMap: { [K in U]: string }, 48) { 49 // We could accept this as an argument, but it's convenient to try to infer it 50 // automatically here, given how narrow our use cases are for calling this fn. 51 const tagKey = Object.hasOwn(taggedUnionValue, '__typename') 52 ? ('__typename' as keyof typeof taggedUnionValue) 53 : ('type' as keyof typeof taggedUnionValue); 54 55 const tagValue = taggedUnionValue[tagKey] as U; 56 57 const inputObjectKey = tagValueToInputKeyMap[tagValue]; 58 59 return { [inputObjectKey]: omit(taggedUnionValue, tagKey) }; 60} 61 62/** 63 * Apollo always adds __typename to the selection set of all queries that it 64 * issues. Sometimes, though, we want to use a query's output, let the end user 65 * modify it, and then pass data back with the same shape as a mutation's input. 66 * But because the corresponding input type for the mutation doesn't have 67 * __typename, the mutation fails. So, this helper function removes __typename 68 * recursively from a query result. 69 */ 70export function stripTypename<T extends object>(it: T): WithoutTypename<T> { 71 return ( 72 Array.isArray(it) 73 ? it.map(stripTypename) 74 : isPlainObject(it) 75 ? mapValues(omit(it, '__typename'), stripTypename) 76 : it 77 ) as WithoutTypename<T>; 78} 79 80export type WithoutTypename<T> = T extends (infer U)[] 81 ? WithoutTypename<U>[] 82 : T extends (...args: any[]) => any 83 ? T 84 : T extends object 85 ? Omit<{ [K in keyof T]: WithoutTypename<T[K]> }, '__typename'> 86 : T;