Mirage服务器可以获取数据,但POST请求失败。

huangapple go评论54阅读模式
英文:

Mirage server GETs data but POST fails

问题

我有mirage模型:

// mirage/models/country.js
import { Model, belongsTo, hasMany } from 'miragejs';

export default Model.extend({
    name: '',
    iso3166_1_alpha3: '',
    capitol_city: belongsTo('city', {inverse: null}),
    cities: hasMany('city', {inverse: 'country'})
});

和:

// mirage/models/city.js
import { Model, belongsTo } from 'miragejs';

export default Model.extend({
    name: '',
    country: belongsTo('country', {inverse: 'cities'})
});

以及序列化器:

// mirage/serializers/application.js
import { camelize, capitalize, underscore } from '@ember/string';
import { JSONAPISerializer } from 'miragejs';

export default class ApplicationSerializer extends JSONAPISerializer
{
    alwaysIncludeLinkageData = true;

    keyForAttribute(attr) {
        return underscore(attr);
    };
    keyForRelationship(modelName) {
        return underscore(modelName);
    };
    typeKeyForModel(model) {
        return capitalize(camelize(model.modelName));
    };
};

当我运行测试时:

import { module, test } from 'qunit';
import { setupTest } from 'ember-qunit';
import { setupMirage } from 'ember-cli-mirage/test-support';

module('Unit | Mirage | mirage models', function (hooks) {
  setupTest(hooks);
  setupMirage(hooks);

  test('it retrieves the country', async function (assert) {
    const server = this.server;
    let city = server.create('city', { id: '1', name: 'Paris' });

    server.create(
        'country',
        {
            id: 'FR',
            name: 'France',
            iso3166_1_alpha3: 'FRA',
            capitol_city: city
        }
    );

    let response = await fetch('/api/countries')
    assert.strictEqual(response.status, 200, "Should have created the model");
    let json = await response.json();
    assert.deepEqual(
        json,
        {
            data: [
                {
                    type: 'Country',
                    id: 'FR',
                    attributes: {
                        name: 'France',
                        iso3166_1_alpha3: 'FRA',
                    },
                    relationships: {
                        capitol_city: {data: {type: 'City', id: '1'}},
                        cities: {data: []},
                    }
                }
            ]
        }
    )
  });

  test('it creates the country', async function (assert) {
    const server = this.server;
    server.create('city', { id: '1', name: 'Paris' });

    let response = await fetch(
        '/api/countries',
        {
            method: 'POST',
            headers: {'Countent-Type': 'application/json'},
            body: JSON.stringify(
                {
                    data: {
                        id: 'FR',
                        type: 'Country',
                        attributes: {
                            iso3166_1_alpha3: 'FRA',
                            name: 'France',

                        },
                        relationships: {
                            capitol_city: { data: { type: 'City', id: '1'} },
                            cities: { data: [{ type: 'City', id: '1'}] }
                        }
                    }
                }
            )
        }
    );

    console.log((await response.json()).message);
    assert.strictEqual(response.status, 201, "Should have created the model");
  });
});

第一个测试通过,第二个测试失败并显示以下消息:

Mirage: You're passing the relationship 'capitol_city' to the 'country' model via a POST to '/api/countries', but you did not define the 'capitol_city' association on the 'country' model.

我该如何让Mirage识别模型上的 capitol_city 属性?

英文:

I have the mirage models:

// mirage/models/country.js
import { Model, belongsTo, hasMany } from 'miragejs';

export default Model.extend({
    name: '',
    iso3166_1_alpha3: '',
    capitol_city: belongsTo('city', {inverse: null}),
    cities: hasMany('city', {inverse: 'country'})
});

and:

// mirage/models/city.js
import { Model, belongsTo } from 'miragejs';

export default Model.extend({
    name: '',
    country: belongsTo('country', {inverse: 'cities'})
});

and the serializer:

// mirage/serializers/application.js
import { camelize, capitalize, underscore } from '@ember/string';
import { JSONAPISerializer } from 'miragejs';

export default class ApplicationSerializer extends JSONAPISerializer
{
    alwaysIncludeLinkageData = true;

    keyForAttribute(attr) {
        return underscore(attr);
    };
    keyForRelationship(modelName) {
        return underscore(modelName);
    };
    typeKeyForModel(model) {
        return capitalize(camelize(model.modelName));
    };
};

When I run the tests:

import { module, test } from 'qunit';
import { setupTest } from 'ember-qunit';
import { setupMirage } from 'ember-cli-mirage/test-support';

module('Unit | Mirage | mirage models', function (hooks) {
  setupTest(hooks);
  setupMirage(hooks);

  test('it retrieves the country', async function (assert) {
    const server = this.server;
    let city = server.create('city', { id: '1', name: 'Paris' });

    server.create(
        'country',
        {
            id: 'FR',
            name: 'France',
            iso3166_1_alpha3: 'FRA',
            capitol_city: city
        }
    );

    let response = await fetch('/api/countries')
    assert.strictEqual(response.status, 200, "Should have created the model");
    let json = await response.json();
    assert.deepEqual(
        json,
        {
            data: [
                {
                    type: 'Country',
                    id: 'FR',
                    attributes: {
                        name: 'France',
                        iso3166_1_alpha3: 'FRA',
                    },
                    relationships: {
                        capitol_city: {data: {type: 'City', id: '1'}},
                        cities: {data: []},
                    }
                }
            ]
        }
    )
  });

  test('it creates the country', async function (assert) {
    const server = this.server;
    server.create('city', { id: '1', name: 'Paris' });

    let response = await fetch(
        '/api/countries',
        {
            method: 'POST',
            headers: {'Countent-Type': 'application/json'},
            body: JSON.stringify(
                {
                    data: {
                        id: 'FR',
                        type: 'Country',
                        attributes: {
                            iso3166_1_alpha3: 'FRA',
                            name: 'France',

                        },
                        relationships: {
                            capitol_city: { data: { type: 'City', id: '1'} },
                            cities: { data: [{ type: 'City', id: '1'}] }
                        }
                    }
                }
            )
        }
    );

    console.log((await response.json()).message);
    assert.strictEqual(response.status, 201, "Should have created the model");
  });
});

The first one passes and the second one fails with the message:

> status
> Mirage: You're passing the relationship 'capitol_city' to the 'country' model via a POST to '/api/countries', but you did not define the 'capitol_city' association on the 'country' model.
>

How can I get Mirage to recognise the capitol_city attribute on the model?

答案1

得分: 1

Mirage对属性格式持有一种坚定的看法,并期望属性采用camelCase格式,而不是snake_case

不幸的是,Ember CLI Mirage模型关系文档没有提及这一期望,而且所有示例都使用了单词属性。更不幸的是,Mirage将在简单的GET请求和直接通过API创建模型时与snake_case属性一起工作;只有当您向服务器发出POST/PUT/PATCH请求时,它才会失败,并且错误消息(令人困惑地)将引用已定义的snake case属性。(请参见Mirage源代码中的失败位置。)

要解决此问题,请将属性转换为camel case:

// mirage/models/country.js
import { Model, belongsTo, hasMany } from 'miragejs';

export default Model.extend({
    name: '',
    iso31661Alpha3: 0,
    capitolCity: belongsTo('city', { inverse: null }),
    cities: hasMany('city', { inverse: 'country' })
});

并且在测试中也进行更改:

import { module, test } from 'qunit';
import { setupTest } from 'ember-qunit';
import { setupMirage } from 'ember-cli-mirage/test-support';

module('Unit | Mirage | mirage models', function (hooks) {
  setupTest(hooks);
  setupMirage(hooks);

  test('it retrieves the country', async function (assert) {
    const server = (this as any).server;
    let city = server.create('city', { id: '1', name: 'Paris' });

    server.create(
        'country',
        {
            id: 'FR',
            name: 'France',
            iso31661Alpha3: 'FRA',
            capitolCity: city
        }
    );

    let response = await fetch('/api/countries')
    assert.strictEqual(response.status, 200, "Should have created the model");
    let json = await response.json();
    console.log(JSON.stringify(json));
    assert.deepEqual(
        json,
        {
            data: [
                {
                    type: 'Country',
                    id: 'FR',
                    attributes: {
                        name: 'France',
                        iso3166_1_alpha3: 'FRA',
                    },
                    relationships: {
                        capitol_city: {data: {type: 'City', id: '1'}},
                        cities: {data: []},
                    }
                }
            ]
        }
    )
  });

  test('it creates the country', async function (assert) {
    const server = (this as any).server;
    let city = server.create('city', { id: '1', name: 'Paris' });

    let response = await fetch(
        '/api/countries',
        {
            method: 'POST',
            headers: {'Countent-Type': 'application/json'},
            body: JSON.stringify(
                {
                    data: {
                        id: 'FR',
                        type: 'Country',
                        attributes: {
                            iso3166_1_alpha3: 'FRA',
                            name: 'France',

                        },
                        relationships: {
                            capitol_city: { data: { type: 'City', id: '1'} },
                            cities: { data: [{ type: 'City', id: '1'}] }
                        }
                    }
                }
            )
        }
    );

    console.log((await response.json()).message);
    assert.strictEqual(response.status, 201, "Should have created the model");
  });
});

然而,一旦您将其转换为camel case,属性iso31661Alpha3在输出中不会被正确格式化,因此您必须手动更改country模型的序列化程序:

// mirage/serializers/country.js
import ApplicationSerializer from './application';

export default class CountrySerializer extends ApplicationSerializer
{
    keyForAttribute(attr: string) {
        switch(attr)
        {
            case 'iso31661Alpha3': return 'iso3166_1_alpha3';
            default: return super.keyForAttribute(attr);
        }
    };
};

一旦属性以正确的格式出现,它就会正常工作。

英文:

Mirage is opinionated with regards to the format of attributes and expects the attributes to be in camelCase (and not snake_case).

Unfortunately the Ember CLI Mirage model relationships documentation does not mention this expectation and all the examples use single-word attributes. Even more unfortunately, Mirage will work with snake_case attributes for simple GET requests and when directly creating models through the API; it is only when you make a request to POST/PUT/PATCH a model into the server that it fails and the message will (confusingly) refer to the snake case attribute which has been defined. (See the Mirage source code for where it fails.)

To solve it, convert the attributes to camel case:

// mirage/models/country.js
import { Model, belongsTo, hasMany } from 'miragejs';

export default Model.extend({
    name: '',
    iso31661Alpha3: 0,
    capitolCity: belongsTo('city', {inverse: null}),
    cities: hasMany('city', {inverse: 'country'})
});

and change it in the tests as well:

import { module, test } from 'qunit';
import { setupTest } from 'ember-qunit';
import { setupMirage } from 'ember-cli-mirage/test-support';

module('Unit | Mirage | mirage models', function (hooks) {
  setupTest(hooks);
  setupMirage(hooks);

  test('it retrieves the country', async function (assert) {
    const server = (this as any).server;
    let city = server.create('city', { id: '1', name: 'Paris' });

    server.create(
        'country',
        {
            id: 'FR',
            name: 'France',
            iso31661Alpha3: 'FRA',
            capitolCity: city
        }
    );

    let response = await fetch('/api/countries')
    assert.strictEqual(response.status, 200, "Should have created the model");
    let json = await response.json();
    console.log(JSON.stringify(json));
    assert.deepEqual(
        json,
        {
            data: [
                {
                    type: 'Country',
                    id: 'FR',
                    attributes: {
                        name: 'France',
                        iso3166_1_alpha3: 'FRA',
                    },
                    relationships: {
                        capitol_city: {data: {type: 'City', id: '1'}},
                        cities: {data: []},
                    }
                }
            ]
        }
    )
  });

  test('it creates the country', async function (assert) {
    const server = (this as any).server;
    let city = server.create('city', { id: '1', name: 'Paris' });

    let response = await fetch(
        '/api/countries',
        {
            method: 'POST',
            headers: {'Countent-Type': 'application/json'},
            body: JSON.stringify(
                {
                    data: {
                        id: 'FR',
                        type: 'Country',
                        attributes: {
                            iso3166_1_alpha3: 'FRA',
                            name: 'France',

                        },
                        relationships: {
                            capitol_city: { data: { type: 'City', id: '1'} },
                            cities: { data: [{ type: 'City', id: '1'}] }
                        }
                    }
                }
            )
        }
    );

    console.log((await response.json()).message);
    assert.strictEqual(response.status, 201, "Should have created the model");
  });
});

However, once you convert it to camel case then the attribute iso31661Alpha3 does not get formatted correctly in the output so you have to manually change the serializer for the country model:

// mirage/serializers/country.js
import ApplicationSerializer from './application';

export default class CountrySerializer extends ApplicationSerializer
{
    keyForAttribute(attr: string) {
        switch(attr)
        {
            case 'iso31661Alpha3': return 'iso3166_1_alpha3';
            default: return super.keyForAttribute(attr);
        }
    };
};

Once the attributes are in the correct case then it will work.

huangapple
  • 本文由 发表于 2023年1月9日 07:20:38
  • 转载请务必保留本文链接:https://go.coder-hub.com/75051984.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定