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

[Swift3] Additional Properties in Swagger specs generating non compiling code #4907

Closed
dwoodel opened this issue Mar 3, 2017 · 27 comments
Closed

Comments

@dwoodel
Copy link
Contributor

dwoodel commented Mar 3, 2017

Description

[Swift3] Additional Properties in Swagger specs generating non compiling code. When using additionalProperties in the swagger specification.

We are using String additonal properties on a model to add generic string fields. This works fine in java where the model use inheritance to extend a HashMap. In swift the primitive dictionary [String : String] is used. This code will not compile, this cannot be done like this. An NSDictionary could be used like this. Unfortunately this is not the only problem.

Swagger-codegen version

Milestone release: 2.2.2

Building the latest 2.2 branch. Commit in question:
6963bf8

Pull Request that seems to have broken the generator.
#4052

Already merged into master.

Swagger declaration file content or url

Example Yaml which generates the problem:

UserResponse:
    type: object
    properties:
      email:
        type: string
        example: jdoe@mail.com
        description: "User's email address"
      firstName:
        type: string
        example: John
        description: First name
      surname:
        type: string
        example: Doe
        description: Surname
      secondName:
        type: string
        description: Second name
      secondSurname:
        type: string
        description: Second surname
      birthday:
        type: string
        example: '1982-05-26'
        description: "User's birthday"
      username:
        type: string
        example: jdoe
        description: Name of user
      phoneNumber:
        type: string
        example: '+375291112233'
        description: "User's phone number"
      status:
        type: string
        example: REGISTERED
        description: "User's status"
    description: Response user model
    additionalProperties:
      type: string
Command line used for generation

generate --input-spec XXXX --lang swift3 --output ./ --artifact-id biid-ios-sdk-public-api-client --group-id com.biid --invoker-package /com.biid.ios.sdk.core.publicapi.client.gen --api-package /com.biid.ios.sdk.core.publicapi.client.api --model-package /com.biid.ios.sdk.core.publicapi.client.model --artifact-version 0.7.48

Steps to reproduce

Make a swagger file which contains additonal properties. Generate the swift3 code. Try to compile!! Collect prize!

Related issues

No releated

Suggest a Fix
@wing328 wing328 added this to the v2.2.3 milestone Mar 6, 2017
@wing328
Copy link
Contributor

wing328 commented Mar 6, 2017

@dwoodel thanks for reporting the issue. Do you mind providing a screen shot of the generated code that has compilation errors?

cc @jaz-ah @Edubits @jgavris @hexelon

@wuf810
Copy link
Contributor

wuf810 commented Mar 6, 2017

Hi I work with @dwoodel .

Enclosed is a screenshot of the Swift code being generated.

screen shot 2017-03-06 at 15 30 53

As you'll see the generated code tries to make the class a subclass of a collection (a dictionary of type String, String). Unlike the equivalent code in java (extends Hashmap), I do not think this is possible in Swift (although it might be possible using an NSDictionary).

Hope this helps.

regards, Michael.

@dwoodel
Copy link
Contributor Author

dwoodel commented Mar 6, 2017

Hi @wing328, yes confirmed @wuf810 is correct here.

@wing328
Copy link
Contributor

wing328 commented Mar 6, 2017

Question on the model definition if you don't mind.

UserResponse is a dictionary/hash/map or it's a model with 9 properties and you want a dictionary/hash/map of this model (using "string" as key)?

@wing328
Copy link
Contributor

wing328 commented Mar 6, 2017

It would help if you can show an example of the JSON for UserResponse.

@dwoodel
Copy link
Contributor Author

dwoodel commented Mar 6, 2017

Hey @wing328 sure no problem...

Yes the user response contains a map of dynamic key [String] :value [String] fields (always strings values despite other JSON types). The keys and the values are totally dynamic and we do not know the keys before hand. Hence they connot be encoded into a model. This works in java because we can just define an additional properties array of type string.

Here is the full model. This works fine in java. The UserResponce object extends a hashmap and just appends the additional properties to it directly. However in swfit the implementation of the UserResponse inheriting from a HashMap (or dictonary in the case of swift) is not possible. You cannot inherit from this type.

You could inherit from something like an NSDictonary which is effectively a map. However then the generator will have a dependency on iOS itself via NSFoundation. I think currently it only has a dependency on the core swift frameworks. Depends what you would like to do.

We have 2 fixed fields on the json... below shows the spec with 2 fixed feilds. And a dynamic array.

"UserResponse": {
      "type": "object",
      "properties": {
        "fixedFeildOne": {
          "type": "string",
          "example": "jdoe@mail.com",
          "description": "User's email address"
        },
        "fixedFeildTwo": {
          "type": "string",
          "example": "John",
          "description": "First name"
        },
      },
      "description": "Response user model",
      "additionalProperties": {
        "type": "string"
      }
    },

Which returns (something like):

User : {
"fixedFeildOne":"foo",
"fixedFeildTwo":"bar",
"dynamicFieldOne":"foobar",
"dynamicFieldTwo":"foobarfoobar",
"dynamicFieldThree":"foobarbarbarfoofoo"
}

@wing328
Copy link
Contributor

wing328 commented Mar 12, 2017

Here is the UserResponse model you provided:

"UserResponse": {
      "type": "object",
      "properties": {
        "fixedFeildOne": {
          "type": "string",
          "example": "jdoe@mail.com",
          "description": "User's email address"
        },
        "fixedFeildTwo": {
          "type": "string",
          "example": "John",
          "description": "First name"
        },
      },
      "description": "Response user model",
      "additionalProperties": {
        "type": "string"
      }
    },

I wonder if you can define the map in a different way instead:

  1. remove additionalProperties from "UserResponse"
  2. define the map as follows:
{
  "type": "object",
  "additionalProperties": {
    "$ref": "#/definitions/UserResponse"
  }
}

@dwoodel
Copy link
Contributor Author

dwoodel commented Mar 14, 2017

Hey @wing328,

Firstly thanks your reply we really appreciate your help, A lot! We regard swagger very highly!

The swift generation contains actual errors. Also we are happy with using the additional properties definition as it works in Java, and we would just like it to work the same way in swift.

Have you guys had a chance to evaluate the swift generator? In cant see it working how it is at the moment. I have built the code that is in master which enables this for swift and from what I can see its just broken.

@wing328
Copy link
Contributor

wing328 commented Mar 14, 2017

@dwoodel

The swift generation contains actual errors

I agree it's an error (which I've not clearly pointed out in my reply, sorry). My suggestion is more a workaround.

I can see the code that fails to compile in #4907 (comment).

What would be the fix looks like? using NSDictionary?

cc @jaz-ah @Edubits @jgavris @hexelon

@wuf810
Copy link
Contributor

wuf810 commented Mar 14, 2017

Using an NSDictionary could work but it really isn't a "swifty" way to do things.

One approach might be for the codegen to add the additionalProperties to the class as collection property i.e. public var additionalProperties : [String : String]

This way they are at least accessible via this property (and iterable, subscriptable) etc.

However I feel there must be a better and more Swift like way to do this…maybe one of the Swift codegen devs might have an idea?

@jgavris
Copy link
Contributor

jgavris commented Mar 14, 2017

I would recommend making these 'additional properties' real fields or another object entirely. Using a dictionary means you're going to step off into a stringly-typed API world and give up all the great things about Swagger.

@wuf810
Copy link
Contributor

wuf810 commented Mar 14, 2017

I was under the impression that additionalProperties is part of the OpenAPI spec and therefore part of Swagger…and supposed to be used when there are custom fields to include.

Custom fields are a requirement of our model.

NB the use of additionalProperties works well for our java generated code (essentially extending the class to a hashmap that means it is possible to iterate over both "fixed" and custom properties).

It just seems that the solution for the generated Swift code is wrong. Are I missing something here?

@jgavris
Copy link
Contributor

jgavris commented Mar 14, 2017

Ah, okay. No, I just was trying to suggest if you absolutely didn't need them you'll be happier without them. The generator is indeed not handling this case.

@wuf810
Copy link
Contributor

wuf810 commented Mar 14, 2017

Thanks Jason. I'm trying to identify what might be the best solution in terms of Swift code (but I'm relatively new to Swift) and then maybe someone more knowledgeable could tackle the how to implement in the codegen template.

@dwoodel
Copy link
Contributor Author

dwoodel commented Mar 15, 2017

Would you like me to create a PR for this. I think I have some time on friday. I can make multiple versions (maybe 2 PR's) with the different templates. Let me know.

Dan

@dwoodel
Copy link
Contributor Author

dwoodel commented Mar 18, 2017

HI @wing328 ,

@wuf810 And I had some time today to work on this today. We have fixed the code generation issue, and the templates issues. The code now generates and compile with no errors. We are not using inheritance we have just created a map member variable. This will work will all standard types. The only issue we have outstanding is fact that the population of these additionalProperties seems to be missing. I will pick this up tomorrow if you like. Can you give me write access to some branch somewhere so I can push up this stuff to be reviewed. Let me know?

@wuf810
Copy link
Contributor

wuf810 commented Mar 21, 2017

As @dwoodel said we spent some time updating the Swift3Codegen to create compilable Swift Code for use with AdditionalProperties and now need to fix the fact that the additionalProperties property is not being populated. I don't think this was ever implemented…although I may have missed this.

Can anyone provide some pointers as where in the code base this happens…it would help with the implementation greatly.

@wing328
Copy link
Contributor

wing328 commented Mar 22, 2017

Can you give me write access to some branch somewhere so I can push up this stuff to be reviewed. Let me know?

@dwoodel you can simply submit a PR with your work (assuming you've forked the repo and make the changes in https://github.com/dwoodel/swagger-codegen for example). Let me know if you need help filing a PR.

@wing328
Copy link
Contributor

wing328 commented Mar 22, 2017

@wuf810 are you referring to a way to tell if a property is a map/dictionary/hash in the mustache template?

The mustache tag {{#isMapContainer}} .. {{/isMapContainer}} may meet your requirement. Here are some examples:

https://github.com/swagger-api/swagger-codegen/search?utf8=%E2%9C%93&q=ismapcontainer&type=

@dwoodel
Copy link
Contributor Author

dwoodel commented Apr 5, 2017

Me and @wuf810 have fixed it. I just forked the repo ill put up a PR tomorrow morning. Sorry only got around to working on it today.

@dwoodel
Copy link
Contributor Author

dwoodel commented Apr 14, 2017

Pull request raised. Sorry about delay.

@dwoodel
Copy link
Contributor Author

dwoodel commented Apr 17, 2017

Just had to be sure of some optional values. Everything is cool. Ready to go.

@dwoodel
Copy link
Contributor Author

dwoodel commented Apr 17, 2017

Travis failed. Using new fork of repo.

@jgavris
Copy link
Contributor

jgavris commented Apr 17, 2017

@dwoodel Slow down...the Swagger test suite is a little flaky. Please don't open another PR, you can just rebase / force push / restart the builds on the PR you just opened. I've already started reviewing that one.

@dwoodel
Copy link
Contributor Author

dwoodel commented Apr 17, 2017

Hey @jgavris Sorry I got an issue to do with auth on github my end. It means I got some issues with pushing to different repos. Ill re-open that if I can. If not ill create one more and I wont close it. I had to rebase my fork.

@dwoodel
Copy link
Contributor Author

dwoodel commented Apr 17, 2017

@jgavris Couldnt re-open. Travis might pass now. Applied to latest master on a new fork. Sorry about all the PR spam.

@wuf810
Copy link
Contributor

wuf810 commented Apr 26, 2017

FYI the latest PR by @dwoodel fixes this issue. #5407

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants