function deserializeValue()

in src/declarative-stack.ts [119:234]


function deserializeValue(stack: cdk.Stack, typeRef: reflect.TypeReference, optional: boolean, key: string, value: any): any {
  // console.error('====== deserializer ===================');
  // console.error(`type: ${typeRef}`);
  // console.error(`value: ${JSON.stringify(value, undefined, 2)}`);
  // console.error('~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~`');

  if (value === undefined) {
    if (optional) {
      return undefined;
    }

    throw new Error(`Missing required value for ${key} in ${typeRef}`);
  }

  // deserialize arrays
  if (typeRef.arrayOfType) {
    if (!Array.isArray(value)) {
      throw new Error(`Expecting array for ${key} in ${typeRef}`);
    }

    return value.map((x, i) => deserializeValue(stack, typeRef.arrayOfType!, false, `${key}[${i}]`, x));
  }

  const asRef = tryResolveRef(value);
  if (asRef) {
    if (isConstruct(typeRef)) {
      return findConstruct(stack, value.Ref);
    }

    throw new Error(
      `{ Ref } can only be used when a construct type is expected and this is ${typeRef}. ` +
      'Use { Fn::GetAtt } to represent specific resource attributes');
  }

  const getAtt = tryResolveGetAtt(value);
  if (getAtt) {
    const [logical, attr] = getAtt;

    if (isConstruct(typeRef)) {
      const obj: any = findConstruct(stack, logical);
      return obj[attr];
    }

    if (typeRef.primitive === 'string') {
      // return a lazy value, so we only try to find after all constructs
      // have been added to the stack.
      return deconstructGetAtt(stack, logical, attr);
    }

    throw new Error(`Fn::GetAtt can only be used for string primitives and ${key} is ${typeRef}`);
  }

  // deserialize maps
  if (typeRef.mapOfType) {
    if (typeof(value) !== 'object') {
      throw new ValidationError(`Expecting object for ${key} in ${typeRef}`);
    }

    const out: any = { };
    for (const [k, v] of Object.entries(value)) {
      out[k] = deserializeValue(stack, typeRef.mapOfType, false, `${key}.${k}`, v);
    }

    return out;
  }

  if (typeRef.unionOfTypes) {
    const errors = new Array<any>();
    for (const x of typeRef.unionOfTypes) {
      try {
        return deserializeValue(stack, x, optional, key, value);
      } catch (e) {
        if (!(e instanceof ValidationError)) {
          throw e;
        }
        errors.push(e);
        continue;
      }
    }

    throw new ValidationError(`Failed to deserialize union. Errors: \n  ${errors.map(e => e.message).join('\n  ')}`);
  }

  const enm = deconstructEnum(stack, typeRef, key, value);
  if (enm) {
    return enm;
  }

  // if this is an interface, deserialize each property
  const ifc = deconstructInterface(stack, typeRef, key, value);
  if (ifc) {
    return ifc;
  }

  // if this is an enum type, use the name to dereference
  if (typeRef.type instanceof reflect.EnumType) {
    const enumType = resolveType(typeRef.type.fqn);
    return enumType[value];
  }

  if (typeRef.primitive) {
    return value;
  }

  const enumLike = deconstructEnumLike(stack, typeRef, value);
  if (enumLike) {
    return enumLike;
  }

  const asType = deconstructType(stack, typeRef, value);
  if (asType) {
    return asType;
  }

  throw new Error(`Unable to deconstruct "${JSON.stringify(value)}" for type ref ${typeRef}`);
}