graphile-i18n 0.0.3 → 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,187 +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')).reduce((m, table) => {
20
- if (table.primaryKeyConstraint?.keyAttributes?.[0]) {
21
- m[table.tags.i18n] = {
22
- identifier: table.primaryKeyConstraint.keyAttributes[0].name,
23
- idType: table.primaryKeyConstraint.keyAttributes[0].type.name
24
- };
25
- }
26
-
27
- return m;
28
- }, {});
29
- const languageVariationTables = tablesWithLanguageTables.map(table => introspection.class.find(t => t.name === table.tags.i18n && t.namespaceName === table.namespaceName));
30
- const i18nTables = {};
31
- const tables = {};
32
- tablesWithLanguageTables.forEach((table, i) => {
33
- i18nTables[table.tags.i18n] = {
34
- table: table.name,
35
- key: null,
36
- // fk_id
37
- connection: inflection.connection(inflection.tableType(table)),
38
- attrs: {},
39
- fields: {},
40
- keyInfo: tablesWithLanguageTablesIdInfo[table.tags.i18n]
41
- };
42
- tables[table.name] = table.tags.i18n;
43
- });
44
- languageVariationTables.forEach(table => {
45
- const foreignConstraintsThatMatter = table.constraints.filter(c => c.type === 'f').filter(c => c.foreignClass.name === i18nTables[table.name].table);
46
-
47
- if (foreignConstraintsThatMatter.length !== 1) {
48
- // not sure why, but sometimes a version of the table appears in the introspection that doesn't have keys...
49
- return;
50
- }
51
-
52
- if (foreignConstraintsThatMatter[0].keyAttributes.length !== 1) {
53
- // not sure why, but sometimes a version of the table appears in the introspection that doesn't have keys...
54
- return;
55
- }
56
-
57
- i18nTables[table.name].key = foreignConstraintsThatMatter[0].keyAttributes[0].name;
58
- const {
59
- identifier,
60
- idType
61
- } = i18nTables[table.name].keyInfo;
62
- if (!identifier) return;
63
- table.attributes.forEach(attr => {
64
- if ([langPluginLanguageCodeColumn, identifier].includes(attr.name)) return;
65
-
66
- if (langPluginAllowedTypes.includes(attr.type.name)) {
67
- i18nTables[table.name].fields[inflection.column(attr)] = {
68
- type: attr.type.name,
69
- attr: attr.name,
70
- isNotNull: attr.isNotNull,
71
- column: inflection.column(attr)
72
- };
73
- i18nTables[table.name].attrs[attr.name] = {
74
- type: attr.type.name,
75
- attr: attr.name,
76
- column: inflection.column(attr)
77
- };
78
- }
79
- });
80
- });
81
- return build.extend(build, {
82
- i18n: {
83
- i18nTables,
84
- tables
85
- }
86
- });
87
- });
88
- builder.hook('GraphQLObjectType:fields', (fields, build, context) => {
89
- const {
90
- graphql: {
91
- GraphQLString,
92
- GraphQLObjectType,
93
- GraphQLNonNull
94
- },
95
- i18n: {
96
- i18nTables,
97
- tables
98
- },
99
- pgSql: sql
100
- } = build;
101
- const {
102
- scope: {
103
- pgIntrospection: table,
104
- isPgRowType
105
- },
106
- fieldWithHooks
107
- } = context;
108
-
109
- if (!isPgRowType || !table || table.kind !== 'class') {
110
- return fields;
111
- }
112
-
113
- const variationsTableName = tables[table.name];
114
-
115
- if (!variationsTableName) {
116
- return fields;
117
- }
118
-
119
- const i18nTable = i18nTables[variationsTableName];
120
- const {
121
- identifier,
122
- idType
123
- } = i18nTable.keyInfo;
124
- const {
125
- key,
126
- connection,
127
- attrs,
128
- fields: i18nFields
129
- } = i18nTable;
130
- const localeFieldName = 'localeStrings';
131
- const localeFieldsType = new GraphQLObjectType({
132
- name: `${context.Self.name}LocaleStrings`,
133
- description: `Locales for ${context.Self.name}`,
134
- fields: Object.keys(i18nFields).reduce((memo, field) => {
135
- memo[field] = {
136
- type: i18nFields[field].isNotNull ? new GraphQLNonNull(GraphQLString) : GraphQLString,
137
- description: `Locale for ${field}`
138
- };
139
- return memo;
140
- }, {
141
- langCode: {
142
- type: GraphQLString // MUST BE NULLABLE
143
-
144
- }
145
- })
146
- });
147
- return build.extend(fields, {
148
- [localeFieldName]: fieldWithHooks(localeFieldName, fieldContext => {
149
- const {
150
- addDataGenerator
151
- } = fieldContext;
152
- addDataGenerator(parsedResolveInfoFragment => {
153
- return {
154
- pgQuery: queryBuilder => {
155
- queryBuilder.select(sql.fragment`${queryBuilder.getTableAlias()}.${sql.identifier(identifier)}`, identifier);
156
- }
157
- };
158
- });
159
- const coalescedFields = Object.keys(i18nFields).map(field => {
160
- const columnName = i18nFields[field].attr;
161
- const escColumnName = escapeIdentifier(columnName);
162
- const escFieldName = escapeIdentifier(field);
163
- return `coalesce(v.${escColumnName}, b.${escColumnName}) as ${escFieldName}`;
164
- });
165
- const props = {
166
- table,
167
- coalescedFields,
168
- variationsTableName,
169
- key
170
- };
171
- return {
172
- description: `Locales for ${context.Self.name}`,
173
- type: new GraphQLNonNull(localeFieldsType),
174
-
175
- async resolve({
176
- id
177
- }, args, context) {
178
- const languageCodes = context.langCodes ?? langPluginDefaultLanguages;
179
- const dataloader = context.getLanguageDataLoader(props, context.pgClient, languageCodes, identifier, idType, langPluginLanguageCodeColumn, langPluginLanguageCodeGqlField);
180
- return dataloader.load(id);
181
- }
182
-
183
- };
184
- }, 'Adding the language code field from the lang plugin')
185
- });
186
- });
187
- };