graphile-i18n 0.0.2 → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,60 +0,0 @@
1
- import DataLoader from 'dataloader';
2
- import langParser from 'accept-language-parser';
3
- import env from './env';
4
-
5
- const escapeIdentifier = str => `"${str.replace(/"/g, '""')}"`;
6
-
7
- export const makeLanguageDataLoaderForTable = _req => {
8
- const cache = new Map();
9
- return (props, pgClient, languageCodes, identifer, idType, sqlField, gqlField) => {
10
- let dataLoader = cache.get(props);
11
-
12
- if (!dataLoader) {
13
- const {
14
- table,
15
- coalescedFields,
16
- variationsTableName,
17
- key
18
- } = props;
19
- const schemaName = escapeIdentifier(table.namespaceName);
20
- const baseTable = escapeIdentifier(table.name);
21
- const variationTable = escapeIdentifier(variationsTableName);
22
- const joinKey = escapeIdentifier(key);
23
- const fields = coalescedFields.join(', ');
24
- const b = [schemaName, baseTable].join('.');
25
- const v = [schemaName, variationTable].join('.');
26
- dataLoader = new DataLoader(async ids => {
27
- const {
28
- rows
29
- } = await pgClient.query(`
30
- select *
31
- from unnest($1::${idType}[]) ids(${identifer})
32
- inner join lateral (
33
- select b.${identifer}, v.${sqlField} as "${gqlField}", ${fields}
34
- from ${b} b
35
- left join ${v} v
36
- on (v.${joinKey} = b.${identifer} and array_position($2, ${sqlField}) is not null)
37
- where b.${identifer} = ids.${identifer}
38
- order by array_position($2, ${sqlField}) asc nulls last
39
- limit 1
40
- ) tmp on (true)
41
- `, [ids, languageCodes]);
42
- return ids.map(id => rows.find(r => r.id === id));
43
- });
44
- cache.set(props, dataLoader);
45
- }
46
-
47
- return dataLoader;
48
- };
49
- };
50
- export const additionalGraphQLContextFromRequest = (req, res) => {
51
- const language = langParser.pick(env.ACCEPTED_LANGUAGES, req.get('accept-language')); // Accept-Language: *
52
- // Accept-Language: en-US,en;q=0.5
53
- // Accept-Language: fr-CH, fr;q=0.9, en;q=0.8, de;q=0.7, *;q=0.5
54
-
55
- return {
56
- langCodes: [language],
57
- // in future make fallback languages
58
- getLanguageDataLoader: makeLanguageDataLoaderForTable(req)
59
- };
60
- };
package/module/plugin.js DELETED
@@ -1,173 +0,0 @@
1
- const escapeIdentifier = str => `"${str.replace(/"/g, '""')}"`;
2
-
3
- export const LangPlugin = (builder, options) => {
4
- const {
5
- langPluginLanguageCodeColumn = 'lang_code',
6
- langPluginLanguageCodeGqlField = 'langCode',
7
- langPluginAllowedTypes = ['citext', 'text'],
8
- langPluginDefaultLanguages = ['en']
9
- } = options;
10
- builder.hook('build', build => {
11
- const {
12
- pgSql: sql
13
- } = build;
14
- /** @type {import('graphile-build-pg').PgIntrospectionResultsByKind} */
15
-
16
- const introspection = build.pgIntrospectionResultsByKind;
17
- const inflection = build.inflection;
18
- const tablesWithLanguageTables = introspection.class.filter(table => table.tags.hasOwnProperty('i18n'));
19
- const tablesWithLanguageTablesIdInfo = introspection.class.filter(table => table.tags.hasOwnProperty('i18n')).map(table => {
20
- return {
21
- identifier: table.primaryKeyConstraint.keyAttributes[0].name,
22
- idType: table.primaryKeyConstraint.keyAttributes[0].type.name
23
- };
24
- });
25
- const languageVariationTables = tablesWithLanguageTables.map(table => introspection.class.find(t => t.name === table.tags.i18n && t.namespaceName === table.namespaceName));
26
- const i18nTables = {};
27
- const tables = {};
28
- tablesWithLanguageTables.forEach((table, i) => {
29
- i18nTables[table.tags.i18n] = {
30
- table: table.name,
31
- key: null,
32
- // action_id
33
- connection: inflection.connection(inflection.tableType(table)),
34
- attrs: {},
35
- fields: {},
36
- keyInfo: tablesWithLanguageTablesIdInfo[i]
37
- };
38
- tables[table.name] = table.tags.i18n;
39
- });
40
- languageVariationTables.forEach(table => {
41
- const foreignConstraintsThatMatter = table.constraints.filter(c => c.type === 'f').filter(c => c.foreignClass.name === i18nTables[table.name].table);
42
- if (foreignConstraintsThatMatter.length !== 1) throw new Error('lang table only supports one foreign key to parent table');
43
- if (foreignConstraintsThatMatter[0].keyAttributes.length !== 1) throw new Error('lang table only supports one non compound foreign key to parent table');
44
- i18nTables[table.name].key = foreignConstraintsThatMatter[0].keyAttributes[0].name;
45
- const {
46
- identifier,
47
- idType
48
- } = i18nTables[table.name].keyInfo;
49
- table.attributes.forEach(attr => {
50
- if ([langPluginLanguageCodeColumn, identifier].includes(attr.name)) return;
51
-
52
- if (langPluginAllowedTypes.includes(attr.type.name)) {
53
- i18nTables[table.name].fields[inflection.column(attr)] = {
54
- type: attr.type.name,
55
- attr: attr.name,
56
- isNotNull: attr.isNotNull,
57
- column: inflection.column(attr)
58
- };
59
- i18nTables[table.name].attrs[attr.name] = {
60
- type: attr.type.name,
61
- attr: attr.name,
62
- column: inflection.column(attr)
63
- };
64
- }
65
- });
66
- });
67
- return build.extend(build, {
68
- i18n: {
69
- i18nTables,
70
- tables
71
- }
72
- });
73
- });
74
- builder.hook('GraphQLObjectType:fields', (fields, build, context) => {
75
- const {
76
- graphql: {
77
- GraphQLString,
78
- GraphQLObjectType,
79
- GraphQLNonNull
80
- },
81
- i18n: {
82
- i18nTables,
83
- tables
84
- },
85
- pgSql: sql
86
- } = build;
87
- const {
88
- scope: {
89
- pgIntrospection: table,
90
- isPgRowType
91
- },
92
- fieldWithHooks
93
- } = context;
94
-
95
- if (!isPgRowType || !table || table.kind !== 'class') {
96
- return fields;
97
- }
98
-
99
- const variationsTableName = tables[table.name];
100
-
101
- if (!variationsTableName) {
102
- return fields;
103
- }
104
-
105
- const i18nTable = i18nTables[variationsTableName];
106
- const {
107
- identifier,
108
- idType
109
- } = i18nTable.keyInfo;
110
- const {
111
- key,
112
- connection,
113
- attrs,
114
- fields: i18nFields
115
- } = i18nTable;
116
- const localeFieldName = 'localeStrings';
117
- const localeFieldsType = new GraphQLObjectType({
118
- name: `${context.Self.name}LocaleStrings`,
119
- description: `Locales for ${context.Self.name}`,
120
- fields: Object.keys(i18nFields).reduce((memo, field) => {
121
- memo[field] = {
122
- type: i18nFields[field].isNotNull ? new GraphQLNonNull(GraphQLString) : GraphQLString,
123
- description: `Locale for ${field}`
124
- };
125
- return memo;
126
- }, {
127
- langCode: {
128
- type: GraphQLString // MUST BE NULLABLE
129
-
130
- }
131
- })
132
- });
133
- return build.extend(fields, {
134
- [localeFieldName]: fieldWithHooks(localeFieldName, fieldContext => {
135
- const {
136
- addDataGenerator
137
- } = fieldContext;
138
- addDataGenerator(parsedResolveInfoFragment => {
139
- return {
140
- pgQuery: queryBuilder => {
141
- queryBuilder.select(sql.fragment`${queryBuilder.getTableAlias()}.${sql.identifier(identifier)}`, identifier);
142
- }
143
- };
144
- });
145
- const coalescedFields = Object.keys(i18nFields).map(field => {
146
- const columnName = i18nFields[field].attr;
147
- const escColumnName = escapeIdentifier(columnName);
148
- const escFieldName = escapeIdentifier(field);
149
- return `coalesce(v.${escColumnName}, b.${escColumnName}) as ${escFieldName}`;
150
- });
151
- const props = {
152
- table,
153
- coalescedFields,
154
- variationsTableName,
155
- key
156
- };
157
- return {
158
- description: `Locales for ${context.Self.name}`,
159
- type: new GraphQLNonNull(localeFieldsType),
160
-
161
- async resolve({
162
- id
163
- }, args, context) {
164
- const languageCodes = context.langCodes ?? langPluginDefaultLanguages;
165
- const dataloader = context.getLanguageDataLoader(props, context.pgClient, languageCodes, identifier, idType, langPluginLanguageCodeColumn, langPluginLanguageCodeGqlField);
166
- return dataloader.load(id);
167
- }
168
-
169
- };
170
- }, 'Adding the language code field from the lang plugin')
171
- });
172
- });
173
- };