koopjs / FeatureServer

An open source Geoservices Implementation (deprecated)
https://geoservices.github.io
Other
104 stars 32 forks source link

Support provider-generated service and layer info responses #8

Closed jkerr5 closed 7 years ago

jkerr5 commented 7 years ago

Allow the provider to control more of the service info response. It should be able to set/override all of the available fields including the available layers and tables.

Allow the provider to control more of the layer/table info response for each of the layers/tables. It should be able to set/override all of the available fields.

dmfenton commented 7 years ago

Can you provide examples of the desired responses?

jkerr5 commented 7 years ago

Starting with the highest priority part (maybe we break this into other issues), the provider needs to be able to specify the list of layers and tables that are available. So, in the service info response, the provider should be able to tell koop to produce something like this:

  "layers": [
    { "id": 0, "name": "Parcels" },
    { "id": 1, "name": "Buildings" }
  ],
  "tables": [
    { "id": 2, "name": "Owners" }
  ]

Less important is serviceDescription which seems to be set to "This is a feature service exposed with Koop, an open source project that turns APIs into features. Service Description information may not be available for all services. For more information, check out https://github.com/koopjs/koop " in the template.

Also, I believe there may be a bug in setting the displayField as the template code is looking for displayFieldName but the actual template has displayField (see https://github.com/FeatureServer/FeatureServer/blob/master/src/templates.js#L50).

Fundamentally, I believe the providers should be able to communicate metadata to koop to be able to customize pretty much any and all fields list in the feature service spec for these responses:

dmfenton commented 7 years ago

I believe this is actually supported already, if you send back an array of geojson objects to a /layers or /featureserver request it should handle the formatting. but of course that's not documented :)

we can both try that out.

I'll take a look at the display field bug and the additional metadata.

dmfenton commented 7 years ago

actually, it's supported on /layers but not the plain server info. I'll add that support in. I'll have to figure out the best way to support it on the provider side. Need to decide to between something like

[
{type: 'FeatureCollection'},
{type: 'FeatureCollection'}
]

and

{
name: foo,
layers: [
{type: 'FeatureCollection'},
{type: 'FeatureCollection'}
]
}

leaning towards the latter so it's easier to support server-wide metadata

jkerr5 commented 7 years ago

Thanks. We need to make sure that the layer number can be set though as the provider has to be the one to map that number to an actual layer in the backend.

dmfenton commented 7 years ago

passing a layer number is already supported. it will be in req.params.layer inside getData

jkerr5 commented 7 years ago

I mean that the provider needs to be able to tell koop what the layer numbers are when producing the service info response. The examples you gave above did not include a way for me to tell koop what the layer numbers are in my response from the getData() call. I guess we could rely on ordering of the layers that we return as long as the provider makes sure it always returns the layer list in the same order.

dmfenton commented 7 years ago

I'm going to add support for the server info response. And yes it will be up to the provider to maintain a mapping between layer index and underlying data. Just pointing out that you could already pass a layer id and use that to control the query response

dmfenton commented 7 years ago

I'll document what the path will be but my thinking is that The provider can either pass a layer id in the metadata object or Koop will assign ids starting from 0

jkerr5 commented 7 years ago

Sounds good.

As a side note, the /layers call doesn't change the "params" at all. The only way (that I see) for the provider to know it is a /layers call is to look at the URL in the req object. That works but you might want to set the "method" for those requests.

dmfenton commented 7 years ago

/layers will change req.url

dmfenton commented 7 years ago

But I'll see if there is a more natural way to pass that. Btw that code lives in https://github.com/koopjs/Koop-output-geoservices

dmfenton commented 7 years ago

Looked at this a bit more. You're going to have to parse /layers out of the URL. I can't make it a parameter because it would conflict with /:layer

jkerr5 commented 7 years ago

OK, will do on the /layers function.

dmfenton commented 7 years ago

This was implemented in bdb4fab711b0de95ba6862bc500b784ca95c838f

jkerr5 commented 7 years ago

Was anything implemented for the /:layer route? I.E. for the FeatureServer/<layer num> requests as detailed here http://resources.arcgis.com/en/help/arcgis-rest-api/#/Layer/02r3000000w6000000/

dmfenton commented 7 years ago

That's all going to be in Provider land. We may need to assign a the same layer id that was requested, but I'm not sure that's going to be necessary for the client applications.

dmfenton commented 7 years ago

Lets say you have two MarkLogic datasets:

0: Foo 1: Bar

/FeatureServer/0 => getData => callback(null, foo)

/FeatureServer/1 => getData => callback(null, bar)

make sense?

jkerr5 commented 7 years ago

As I understand so far, for a call to /FeatureServer, the provider should respond with an object like this:

const server = {
  description: String // Describes the collection of layers below,
  layers: [{ // A collection of all the layers managed by the server
    type: 'FeatureCollection',
    metadata: {
      name: String, // The name of the layer
      description: String // The description of the layer
      extent:  Object || Array // valid extent object or 2 coord array
      displayField: String // The display field to be used by a client
      id: String // unique identifier field
    }
    features: [// If all the metadata provided above is provided features are optional.
      {
        type: 'Feature',
        geometry: {
          type: 'Point',
          coordinates: [125.6, 10.1]
        },
        properties: {
          name: 'Dinagat Islands'
        }
      }]
    }
  }]
}

Are you saying for a call to /FeatureServer/0, it should use the same structure but just include the info for layer "0"?

dmfenton commented 7 years ago

No, simply pass geojson, with metadata, as you did before.

dmfenton commented 7 years ago

I'm adding support for a metadata annotation of geometry type so you won't need to pass any features at all if you choose.

jkerr5 commented 7 years ago

So, if wanted to produce the timeInfo section like:

   "timeInfo" : {
     "startTimeField" : "<startTimeFieldName>",
     "endTimeField" : "<endTimeFieldName>",
     "trackIdField" : "<trackIdFieldName>",
     "timeExtent" : [<startTime>, <endTime>],
     "timeReference" : {
       "timeZone" : "<timeZone>",
       "respectsDaylightSaving" : <true | false>
     },
     "timeInterval" : <timeInterval>,
     "timeIntervalUnits" : "<timeIntervalUnits>"
   },

for layer "0", how would I return that from the provider to koop?

dmfenton commented 7 years ago

not supported yet. please open an issue on featureserver

jkerr5 commented 7 years ago

Submitted issue #11

labbr01 commented 4 years ago

I am looking help trying to create a oracle (non SDE) provider.

So far, i have been able to create a provider able to read data using de koop-provider-socrata example and populating «Model.prototype.getData» method with code accessing the database.

Now i want to create new route for addFeature, deleteFeatures and updateFeatures method.

Here the output of the Debug Console once i have added routes: "Geoservices" output routes for the "provider-socrata" provider Methods


/provider-socrata/rest/info GET, POST /provider-socrata/tokens/:method GET, POST /provider-socrata/tokens/ GET, POST /provider-socrata/rest/services/:host/FeatureServer/:layer/:method GET, POST /provider-socrata/rest/services/:host/FeatureServer/layers GET, POST /provider-socrata/rest/services/:host/FeatureServer/:layer GET, POST /provider-socrata/rest/services/:host/FeatureServer GET, POST /provider-socrata/:host/FeatureServer/:layer/:method GET, POST /provider-socrata/:host/FeatureServer/layers GET, POST /provider-socrata/:host/FeatureServer/:layer GET, POST /provider-socrata/:host/FeatureServer GET, POST /provider-socrata/rest/services/:host/FeatureServer GET, POST /provider-socrata/:host/FeatureServer GET, POST /provider-socrata/rest/services/:host/MapServer GET, POST /provider-socrata/:host/MapServer GET, POST

index.js:239

"provider-socrata" provider routes Methods


/provider-socrata/rest/services/:id/FeatureServer/:layer/addFeatures POST
/provider-socrata/rest/services/:id/FeatureServer/:layer/deleteFeatures POST
/provider-socrata/rest/services/:id/FeatureServer/:layer/updateFeatures POST

My problem is the code is not accessing the method of my controler (code example partial) Controller.prototype.updateFeatures = function (req, res) { res.status(200).send('Welcome to Koop update feature!'); };

It like if the «"Geoservices" output routes for the "provider-socrata" provider Methods » have priority over the «"provider-socrata" provider routes Methods», so when i call URL Like «http://localhost:8080/provider-socrata/rest/services/oracle/FeatureServer/0/updateFeatures» i dont enter into my method, but i enter in the «getData» method probably due to the «/provider-socrata/rest/services/:host/FeatureServer/:layer/:method GET, POST» GeoService route.

I did'nt find any example how to change route priority in order to direct the request into my method.

Yes i am trying to make an CRUD provider that will work with Oracle without SDE installation.

I would appreciate any help or link to documentation on how the route parser work.

Thank.

rgwozdz commented 4 years ago

Yes, as you note, the issue is that the GeoServices output route /provider-socrata/rest/services/:host/FeatureServer/:layer/:method is handling any requests to /provider-socrata/rest/services/:id/FeatureServer/:layer/addFeatures. This is because it is registered first and addFeatures is captured by the :method parameter.

Currently, there is no way to change the route priority. Output plugins have to be registered before provider plugins, otherwise those output-plugin routes won't get attached to the provider. We could try to change the way registration is executed, but it would be a big change. cc @haoliangyu

rgwozdz commented 4 years ago

@labbr01 - this should probably be it's own issue.

rgwozdz commented 4 years ago

@labbr01 - after some review of the code, this may be easier then I originally surmised. Moved this issue to https://github.com/koopjs/FeatureServer/issues/171.

rgwozdz commented 4 years ago

@labbr01 - your issue should be resolved with koop-core release 3.14.0.