Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Include the seeding helper #55

Open
lolmaus opened this issue Feb 24, 2020 · 0 comments
Open

Include the seeding helper #55

lolmaus opened this issue Feb 24, 2020 · 0 comments
Labels
enhancement New feature or request

Comments

@lolmaus
Copy link
Contributor

lolmaus commented Feb 24, 2020

/* eslint-disable @typescript-eslint/no-explicit-any */

// @ts-ignore
import tableConverter from 'yadda/lib/converters/table-converter';

import { Server, ModelInstance, AnyAttrs, ModelInstanceShared } from 'ember-cli-mirage';
import { dasherize, camelize } from '@ember/string';
import { pluralize } from 'ember-inflector';
import { assert } from '@ember/debug';

// @ts-ignore
import HasMany from 'ember-cli-mirage/orm/associations/has-many';

// @ts-ignore
import BelongsTo from 'ember-cli-mirage/orm/associations/belongs-to';

import { Dict } from '@glimmer/interfaces';

const REGEX_COMMA_AND_SEPARATOR = /\s*,\s*|\s+and\s+/g;
const REGEX_REL_NAME = /(.+?)\((.+?)\)/;
const REGEX_ID_AND_TYPE = /@([^()]+)(?:\((.+?)\))?/;

function findRelationship(server: Server, type: string, relationshipName: string): HasMany | BelongsTo | undefined {
  try {
    const Model = server.schema.modelClassFor(type);
    return Model.associationFor(relationshipName);
  } catch (e) {} // eslint-disable-line no-empty
}

function findRelatedRecord(
  server: Server,
  idRaw: string,
  relatedTypeFromRelationship: string
): ModelInstanceShared<AnyAttrs> {
  const result = REGEX_ID_AND_TYPE.exec(idRaw);

  if (!result || !result[1]) {
    throw new Error(`Invalid id: ${idRaw}`);
  }

  const [, id, typeFromId] = result;

  const relatedType = typeFromId || relatedTypeFromRelationship;

  const relatedTypePlural = pluralize(camelize(relatedType));
  const relatedCollection = server.schema[relatedTypePlural];

  if (!relatedCollection) {
    throw new Error(`Collection ${relatedTypePlural} does not exist in Mirage Schema`);
  }

  const relatedRecord = relatedCollection.find(id);

  if (!relatedRecord) {
    throw new Error(`Record of type ${relatedType} with id ${id} not found in Mirage Schema`);
  }

  return relatedRecord;
}

function findRelatedRecords(
  server: Server,
  type: string,
  relationshipName: string,
  idOrIdsRaw: string
): [ModelInstance | ModelInstance[] | null, string] {
  idOrIdsRaw = idOrIdsRaw.trim();

  let result;
  let relationship;
  let relatedType: string;

  if (REGEX_REL_NAME.test(relationshipName)) {
    const result = REGEX_REL_NAME.exec(relationshipName);

    if (!result) {
      throw new Error(`Regex parse error for realtionship name '${relationshipName}'`);
    }

    relationshipName = result[1];
    relationship = findRelationship(server, type, relationshipName);
    assert(`No such relationship "${relationshipName}" on Mirage model ${type}`, relationship);
    relatedType = dasherize(result[2]);
  } else {
    relationship = findRelationship(server, type, relationshipName);
    assert(`No such relationship "${relationshipName}" on Mirage model ${type}`, relationship);
    relatedType = relationship.modelName;
  }

  // HasMany
  if (relationship instanceof HasMany) {
    result = idOrIdsRaw
      .split(REGEX_COMMA_AND_SEPARATOR)
      .filter((str) => str.length)
      .map((idRaw: string) => findRelatedRecord(server, idRaw, relatedType));

    // BelongsTo non-empty
  } else if (idOrIdsRaw.length) {
    result = findRelatedRecord(server, idOrIdsRaw, relatedType);

    // BelongsTo empty
  } else {
    result = null;
  }

  return [result, relationshipName];
}

function seedFromRows(server: Server, typeRaw: string, rows: Array<any>): void {
  const type = dasherize(typeRaw);
  const typePlural = pluralize(camelize(typeRaw));

  assert(`Collection ${typePlural} does not exist in Mirage`, !!server.db[typePlural]);

  rows.forEach((row) => {
    let traits: string[] = [];

    const properties = Object.entries(row).reduce((result: Dict<any>, [key, value]: [string, any]) => {
      key = key.trim();
      value = value.trim();

      // Relationship
      if (REGEX_REL_NAME.test(key) || findRelationship(server, type, key)) {
        [value, key] = findRelatedRecords(server, type, key, value);
        // Traits
      } else if (key === 'trait' || key === 'traits') {
        traits = value.split(REGEX_COMMA_AND_SEPARATOR).filter((str: string) => str.length);

        // Empty cell
      } else if (value.length === 0) {
        value = null;

        // Numbers, Strings, Booleans, Arrays and Objects
      } else {
        try {
          value = JSON.parse(value);
        } catch (e) {
          throw new Error(`Invalid JSON passed as "${key}"`);
        }
      }

      result[key] = value;
      return result;
    }, {});

    // @ts-ignore
    delete properties.trait;

    // @ts-ignore
    delete properties.traits;

    server.create(type, properties, ...traits);
  });
}

export default function seedFromOpinionatedTable(server: Server, modelName: string, tableString: string): void {
  let rows: Array<any>;

  tableConverter(tableString.slice(1).trimRight(), (err: Error | null, result: Array<any>) => {
    if (err) {
      throw err;
    }

    rows = result;
  });

  // @ts-ignore
  seedFromRows(server, modelName, rows);
}
@lolmaus lolmaus added the enhancement New feature or request label Feb 24, 2020
@lolmaus lolmaus added this to the First stable release milestone Feb 24, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

1 participant