Generated Model Bug (#15099)

* updates path help service to handle setting id of model

* adds changelog entry

* removes changelog entry
This commit is contained in:
Jordan Reimer 2022-04-20 09:56:03 -06:00 committed by GitHub
parent 6c7dee4824
commit 6cfa604044
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 102 additions and 4 deletions

View File

@ -13,6 +13,7 @@ import fieldToAttrs from 'vault/utils/field-to-attrs';
import { resolve, reject } from 'rsvp';
import { debug } from '@ember/debug';
import { dasherize, capitalize } from '@ember/string';
import { computed } from '@ember/object'; // eslint-disable-line
import { singularize } from 'ember-inflector';
import { withModelValidations } from 'vault/decorators/model-validations';
@ -270,7 +271,7 @@ export default Service.extend({
},
urlForCreateRecord(modelType, snapshot) {
const { id } = snapshot;
const id = snapshot.record.mutableId; // computed property that returns either id or private settable _id value
const path = createPath.path.slice(1, createPath.path.indexOf('{') - 1);
return `${this.buildURL()}/${apiPath}${path}/${id}`;
},
@ -279,6 +280,17 @@ export default Service.extend({
const path = deletePath.path.slice(1, deletePath.path.indexOf('{') - 1);
return `${this.buildURL()}/${apiPath}${path}/${id}`;
},
createRecord(store, type, snapshot) {
return this._super(...arguments).then((response) => {
// if the server does not return an id and one has not been set on the model we need to set it manually from the mutableId value
if (!response?.id && !snapshot.record.id) {
snapshot.record.id = snapshot.record.mutableId;
snapshot.id = snapshot.record.id;
}
return response;
});
},
});
},
@ -302,7 +314,8 @@ export default Service.extend({
const validations = fieldGroups.reduce((obj, element) => {
if (element.default) {
element.default.forEach((v) => {
obj[v.name] = [{ type: 'presence', message: `${v.name} can't be black` }];
const key = v.options.fieldValue || v.name;
obj[key] = [{ type: 'presence', message: `${v.name} can't be blank` }];
});
}
return obj;
@ -315,6 +328,18 @@ export default Service.extend({
} catch (err) {
// eat the error, fieldGroups is computed in the model definition
}
// attempting to set the id prop on a model will trigger an error
// this computed will be used in place of the the id fieldValue -- see openapi-to-attrs
newModel.reopen({
mutableId: computed('id', '_id', {
get() {
return this._id || this.id;
},
set(key, value) {
return (this._id = value);
},
}),
});
newModel.reopenClass({ merged: true });
owner.unregister(modelName);
owner.register(modelName, newModel);

View File

@ -29,7 +29,7 @@ export const expandOpenApiProps = function (props) {
editType,
helpText: description,
possibleValues: prop['enum'],
fieldValue: isId ? 'id' : null,
fieldValue: isId ? 'mutableId' : null,
fieldGroup: group || 'default',
readOnly: isId,
defaultValue: value || null,

View File

@ -3,7 +3,7 @@
{{#each-in fieldGroup as |group fields|}}
{{#if (or (eq group "default") (eq group "Options"))}}
{{#each fields as |attr|}}
{{#if (not-eq attr.options.fieldValue "id")}}
{{#if (not (includes attr.options.fieldValue (array "id" "mutableId")))}}
<InfoTableRow
@alwaysRender={{this.showAllFields}}
@label={{capitalize (or attr.options.label (humanize (dasherize attr.name)))}}

View File

@ -0,0 +1,73 @@
import { module, test } from 'qunit';
import { setupTest } from 'ember-qunit';
import { setupMirage } from 'ember-cli-mirage/test-support';
const openapiStub = {
openapi: {
components: {
schemas: {
UsersRequest: {
type: 'object',
properties: {
password: {
description: 'Password for the user',
type: 'string',
'x-vault-displayAttrs': { sensitive: true },
},
},
},
},
},
paths: {
'/users/{username}': {
post: {
requestBody: {
content: {
'application/json': {
schema: { $ref: '#/components/schemas/UsersRequest' },
},
},
},
},
parameters: [
{
description: 'Username for this user.',
in: 'path',
name: 'username',
required: true,
schema: { type: 'string' },
},
],
'x-vault-displayAttrs': { itemType: 'User', action: 'Create' },
},
},
},
};
module('Unit | Service | path-help', function (hooks) {
setupTest(hooks);
setupMirage(hooks);
hooks.beforeEach(function () {
this.pathHelp = this.owner.lookup('service:path-help');
this.store = this.owner.lookup('service:store');
});
test('it should generate model with mutableId', async function (assert) {
assert.expect(2);
this.server.get('/auth/userpass/', () => openapiStub);
this.server.get('/auth/userpass/users/example', () => openapiStub);
this.server.post('/auth/userpass/users/test', () => {
assert.ok(true, 'POST request made to correct endpoint');
return;
});
const modelType = 'generated-user-userpass';
await this.pathHelp.getNewModel(modelType, 'userpass', 'auth/userpass/', 'user');
const model = this.store.createRecord(modelType);
model.set('mutableId', 'test');
await model.save();
assert.equal(model.get('id'), 'test', 'model id is set to mutableId value on save success');
});
});