@graphile/postgis
Version:
PostGIS support for PostGraphile
219 lines • 11.5 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
const debug_1 = require("./debug");
const utils_1 = require("./utils");
const makeGeoJSONType_1 = require("./makeGeoJSONType");
function identity(input) {
return input;
}
const plugin = builder => {
builder.hook("build", build => {
const GeoJSON = makeGeoJSONType_1.default(build.graphql, build.inflection.builtin("GeoJSON"));
build.addType(GeoJSON);
return build.extend(build, {
getPostgisTypeByGeometryType(pgGISType, subtype, hasZ = false, hasM = false, srid = 0) {
const typeModifier = utils_1.getGISTypeModifier(subtype, hasZ, hasM, srid);
return this.pgGetGqlTypeByTypeIdAndModifier(pgGISType.id, typeModifier);
},
pgGISIncludedTypes: [],
pgGISIncludeType(Type) {
this.pgGISIncludedTypes.push(Type);
},
});
});
builder.hook("init", (_, build) => {
const { newWithHooks, pgIntrospectionResultsByKind: introspectionResultsByKind, graphql: { GraphQLInt, GraphQLNonNull, GraphQLInterfaceType, GraphQLObjectType, }, pgRegisterGqlTypeByTypeId, pgRegisterGqlInputTypeByTypeId, pgTweaksByTypeIdAndModifer, getTypeByName, pgSql: sql, pg2gql, pg2GqlMapper, inflection, pgGISGraphQLTypesByTypeAndSubtype: constructedTypes, pgGISGraphQLInterfaceTypesByType: _interfaces, pgGISGeometryType: GEOMETRY_TYPE, pgGISGeographyType: GEOGRAPHY_TYPE, pgGISExtension: POSTGIS, pgGISIncludeType: includeType, } = build;
if (!GEOMETRY_TYPE || !GEOGRAPHY_TYPE) {
return _;
}
debug_1.default("PostGIS plugin enabled");
const GeoJSON = getTypeByName(inflection.builtin("GeoJSON"));
const geojsonFieldName = inflection.geojsonFieldName();
function getGisInterface(type) {
const zmflag = -1; // no dimensional constraint; could be xy/xyz/xym/xyzm
if (!_interfaces[type.id]) {
_interfaces[type.id] = {};
}
if (!_interfaces[type.id][zmflag]) {
_interfaces[type.id][zmflag] = newWithHooks(GraphQLInterfaceType, {
name: inflection.gisInterfaceName(type),
fields: {
[geojsonFieldName]: {
type: GeoJSON,
description: "Converts the object to GeoJSON",
},
srid: {
type: new GraphQLNonNull(GraphQLInt),
description: "Spatial reference identifier (SRID)",
},
},
resolveType(value, _info) {
const Type = constructedTypes[type.id] &&
constructedTypes[type.id][value.__gisType];
return Type;
},
description: `All ${type.name} types implement this interface`,
}, {
isPgGISInterface: true,
pgGISType: type,
pgGISZMFlag: zmflag,
});
// Force creation of all GraphQL types that could be resolved from this interface
const subtypes = [1, 2, 3, 4, 5, 6, 7];
for (const subtype of subtypes) {
for (const hasZ of [false, true]) {
for (const hasM of [false, true]) {
const typeModifier = utils_1.getGISTypeModifier(subtype, hasZ, hasM, 0);
const Type = getGisType(type, typeModifier);
includeType(Type);
}
}
}
}
return _interfaces[type.id][zmflag];
}
function getGisDimensionInterface(type, hasZ, hasM) {
const zmflag = (hasZ ? 2 : 0) + (hasM ? 1 : 0); // Equivalent to ST_Zmflag: https://postgis.net/docs/ST_Zmflag.html
const coords = { 0: "XY", 1: "XYM", 2: "XYZ", 3: "XYZM" }[zmflag];
if (!_interfaces[type.id]) {
_interfaces[type.id] = {};
}
if (!_interfaces[type.id][zmflag]) {
_interfaces[type.id][zmflag] = newWithHooks(GraphQLInterfaceType, {
name: inflection.gisDimensionInterfaceName(type, hasZ, hasM),
fields: {
[geojsonFieldName]: {
type: GeoJSON,
description: "Converts the object to GeoJSON",
},
srid: {
type: new GraphQLNonNull(GraphQLInt),
description: "Spatial reference identifier (SRID)",
},
},
resolveType(value, _info) {
const Type = constructedTypes[type.id] &&
constructedTypes[type.id][value.__gisType];
return Type;
},
description: `All ${type.name} ${coords} types implement this interface`,
}, {
isPgGISDimensionInterface: true,
pgGISType: type,
pgGISZMFlag: zmflag,
});
// Force creation of all GraphQL types that could be resolved from this interface
const subtypes = [1, 2, 3, 4, 5, 6, 7];
for (const subtype of subtypes) {
const typeModifier = utils_1.getGISTypeModifier(subtype, hasZ, hasM, 0);
const Type = getGisType(type, typeModifier);
includeType(Type);
}
}
return _interfaces[type.id][zmflag];
}
function getGisType(type, typeModifier) {
const typeId = type.id;
const typeDetails = utils_1.getGISTypeDetails(typeModifier);
const { subtype, hasZ, hasM, srid } = typeDetails;
debug_1.default(`Getting ${type.name} type ${type.id}|${typeModifier}|${subtype}|${hasZ}|${hasM}|${srid}`);
if (!constructedTypes[type.id]) {
constructedTypes[type.id] = {};
}
const typeModifierKey = typeModifier != null ? typeModifier : -1;
if (!pgTweaksByTypeIdAndModifer[typeId]) {
pgTweaksByTypeIdAndModifer[typeId] = {};
}
if (!pgTweaksByTypeIdAndModifer[typeId][typeModifierKey]) {
pgTweaksByTypeIdAndModifer[typeId][typeModifierKey] = (fragment, _resolveData) => {
const params = [
sql.literal("__gisType"),
sql.fragment `${sql.identifier(POSTGIS.namespaceName || "public", "postgis_type_name" // MUST be lowercase!
)}(
${sql.identifier(POSTGIS.namespaceName || "public", "geometrytype" // MUST be lowercase!
)}(${fragment}),
${sql.identifier(POSTGIS.namespaceName || "public", "st_coorddim" // MUST be lowercase!
)}(${fragment}::text)
)`,
sql.literal("__srid"),
sql.fragment `${sql.identifier(POSTGIS.namespaceName || "public", "st_srid" // MUST be lowercase!
)}(${fragment})`,
sql.literal("__geojson"),
sql.fragment `${sql.identifier(POSTGIS.namespaceName || "public", "st_asgeojson" // MUST be lowercase!
)}(${fragment})::JSON`,
];
return sql.fragment `(case when ${fragment} is null then null else json_build_object(
${sql.join(params, ", ")}
) end)`;
};
}
const gisTypeKey = typeModifier != null ? utils_1.getGISTypeName(subtype, hasZ, hasM) : -1;
if (!constructedTypes[type.id][gisTypeKey]) {
if (typeModifierKey === -1) {
constructedTypes[type.id][gisTypeKey] = getGisInterface(type);
}
else if (subtype === 0) {
constructedTypes[type.id][gisTypeKey] = getGisDimensionInterface(type, hasZ, hasM);
}
else {
const intType = introspectionResultsByKind.type.find((t) => t.name === "int4" && t.namespaceName === "pg_catalog");
const jsonType = introspectionResultsByKind.type.find((t) => t.name === "json" && t.namespaceName === "pg_catalog");
constructedTypes[type.id][gisTypeKey] = newWithHooks(GraphQLObjectType, {
name: inflection.gisType(type, subtype, hasZ, hasM, srid),
interfaces: () => [
getGisInterface(type),
getGisDimensionInterface(type, hasZ, hasM),
],
fields: {
[geojsonFieldName]: {
type: GeoJSON,
resolve: (data, _args, _context, _resolveInfo) => {
return pg2gql(data.__geojson, jsonType);
},
},
srid: {
type: new GraphQLNonNull(GraphQLInt),
resolve: (data, _args, _context, _resolveInfo) => {
return pg2gql(data.__srid, intType);
},
},
},
}, {
isPgGISType: true,
pgGISType: type,
pgGISTypeDetails: typeDetails,
});
}
}
return constructedTypes[type.id][gisTypeKey];
}
debug_1.default(`Registering handler for ${GEOGRAPHY_TYPE.id}`);
pgRegisterGqlInputTypeByTypeId(GEOGRAPHY_TYPE.id, () => GeoJSON);
pg2GqlMapper[GEOGRAPHY_TYPE.id] = {
map: identity,
unmap: (o) => sql.fragment `st_geomfromgeojson(${sql.value(JSON.stringify(o))}::text)::${sql.identifier(POSTGIS.namespaceName || "public", "geography")}`,
};
pgRegisterGqlTypeByTypeId(GEOGRAPHY_TYPE.id, (_set, typeModifier) => {
return getGisType(GEOGRAPHY_TYPE, typeModifier);
});
debug_1.default(`Registering handler for ${GEOMETRY_TYPE.id}`);
pgRegisterGqlInputTypeByTypeId(GEOMETRY_TYPE.id, () => GeoJSON);
pg2GqlMapper[GEOMETRY_TYPE.id] = {
map: identity,
unmap: (o) => sql.fragment `st_geomfromgeojson(${sql.value(JSON.stringify(o))}::text)`,
};
pgRegisterGqlTypeByTypeId(GEOMETRY_TYPE.id, (_set, typeModifier) => {
return getGisType(GEOMETRY_TYPE, typeModifier);
});
return _;
}, ["PostgisTypes"], ["PgTables"], ["PgTypes"]);
builder.hook("GraphQLSchema", (schema, build) => {
if (!schema.types) {
schema.types = [];
}
schema.types = [...schema.types, ...build.pgGISIncludedTypes];
return schema;
});
};
exports.default = plugin;
//# sourceMappingURL=PostgisRegisterTypesPlugin.js.map