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

Add privateAttributes to global and per model response #7331

Merged
merged 18 commits into from
Sep 24, 2020
Merged
Show file tree
Hide file tree
Changes from 14 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
19 changes: 19 additions & 0 deletions docs/v3.x/concepts/configurations.md
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,25 @@ module.exports = ({ env }) => ({
| `admin.forgotPassword.from` | Sender mail address | string | Default value defined in your [provider configuration](../plugins/email.md#configure-the-plugin) |
| `admin.forgotPassword.replyTo` | Default address or addresses the receiver is asked to reply to | string | Default value defined in your [provider configuration](../plugins/email.md#configure-the-plugin) |

## API

**Path —** `./config/api.js`.

```js
module.exports = ({ env }) => ({
responses: {
privateAttributes: ['_v', 'id', 'created_at'],
},
});
```

**Available options**

| Property | Description | Type | Default |
| ----------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------ | ------- |
| `responses` | Global API response configuration | Object | |
| `responses.privateAttributes` | Set of globally defined attributes to be treated as private. E.g. `_v` when using MongoDb or timestamps like `created_at`, `updated_at` can be treated as private | String array | `[]` |

## Functions

The `./config/functions/` folder contains a set of JavaScript files in order to add dynamic and logic based configurations.
Expand Down
10 changes: 8 additions & 2 deletions docs/v3.x/concepts/models.md
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ Use the CLI, and run the following command `strapi generate:model restaurant nam

This will create two files located at `./api/restaurant/models`:

- `Restaurant.settings.json`: contains the list of attributes and settings. The JSON format makes the file easily editable.
- `Restaurant.settings.json`: contains the list of attributes and settings. The JSON format makes the file easily editable.
- `Restaurant.js`: imports `Restaurant.settings.json` and extends it with additional settings and life cycle callbacks.

::: tip
Expand Down Expand Up @@ -164,12 +164,18 @@ The options key on the model-json states.

- `timestamps`: This tells the model which attributes to use for timestamps. Accepts either `boolean` or `Array` of strings where first element is create date and second element is update date. Default value when set to `true` for Bookshelf is `["created_at", "updated_at"]` and for MongoDB is `["createdAt", "updatedAt"]`.

- `privateAttributes`: This configuration allows to treat a set of attributes as private, even if they're not actually defined as attributes in the model. Accepts an `Array` of strings. It could be used to remove from API responses timestamps or `_v` when using MongoDB. The set of `privateAttributes` defined in the model are merged with the `privateAttributes` defined in the global Strapi configuration.

- `ignorePrivateFor`: This configuration allows to ignore some attributes from the global `privateAttributes` configuration. Accepts an `Array` of strings. The attributes defined in this `Array` will be removed from the `Array` of the global and local `privateAttributes` configuration.

**Path —** `User.settings.json`.

```json
{
"options": {
"timestamps": true
"timestamps": true,
"privateAttributes": ["id", "created_at"],
"ignorePrivateFor": ["some_global_private"]
}
}
```
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,9 @@ describe('Permissions Manager', () => {
options: {},
};
},
config: {
get: jest.fn,
},
};

test('Pick all fields (output) using default model', () => {
Expand Down
36 changes: 34 additions & 2 deletions packages/strapi-plugin-graphql/services/type-definitions.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,14 @@ const buildTypeDefObj = model => {
typeDef[updatedAtKey] = 'DateTime!';
}

const ignoredPrivateAttributes = getIgnoredPrivateAttributes(model);

Object.keys(attributes)
.filter(attributeName => attributes[attributeName].private !== true)
.filter(
attributeName =>
attributes[attributeName].private !== true &&
!ignoredPrivateAttributes.includes(attributeName)
)
.forEach(attributeName => {
const attribute = attributes[attributeName];
// Convert our type to the GraphQL type.
Expand All @@ -56,17 +62,43 @@ const buildTypeDefObj = model => {
// Change field definition for collection relations
associations
.filter(association => association.type === 'collection')
.filter(association => attributes[association.alias].private !== true)
.filter(
association =>
attributes[association.alias].private !== true &&
!ignoredPrivateAttributes.includes(association.alias)
)
.forEach(association => {
typeDef[`${association.alias}(sort: String, limit: Int, start: Int, where: JSON)`] =
typeDef[association.alias];

delete typeDef[association.alias];
});

// Remove private attributes (which are not attributes) defined per model or globally
const privateAttributes = getPrivateAttributes(model);
privateAttributes.forEach(attr => {
if (typeDef[attr]) {
delete typeDef[attr];
}
});

return typeDef;
};

const getIgnoredPrivateAttributes = model => {
return _.get(model, 'options.ignorePrivateFor', []);
};

const getPrivateAttributes = model => {
const allPrivatesAttributes = _.union(
strapi.config.get('api.responses.privateAttributes', []),
_.get(model, 'options.privateAttributes', [])
);
const ignoredPrivateAttributes = getIgnoredPrivateAttributes(model);

return _.difference(allPrivatesAttributes, ignoredPrivateAttributes);
};

const generateEnumDefinitions = (attributes, globalId) => {
return Object.keys(attributes)
.filter(attribute => attributes[attribute].type === 'enumeration')
Expand Down