GithubHelp home page GithubHelp logo

codearte / accurest Goto Github PK

View Code? Open in Web Editor NEW
99.0 20.0 23.0 2.18 MB

Accurest - Consumer Driven Contracts verifier for Java. Moved to:

Home Page: https://github.com/spring-cloud/spring-cloud-contract

License: Apache License 2.0

Groovy 83.05% Java 16.87% Shell 0.09%

accurest's Introduction

ARCHIVED: NOT IN DEVELOPMENT

This project has moved to https://github.com/spring-cloud/spring-cloud-contract

Accurest

Build Status Maven Central Join the chat at https://gitter.im/Codearte/accurest

Consumer Driven Contracts verifier for Java

To make a long story short - Accurest is a tool for Consumer Driven Contract (CDC) development. Accurest ships an easy DSL for describing REST contracts for JVM-based applications. Since version 1.0.7 it also supports messaging.

The contract DSL is used by Accurest for two things:

  1. generating WireMock's JSON stub definitions / stubbed messaging endpoints, allowing rapid development of the consumer side, generating JUnit / Spock's acceptance tests for the server - to verify if your API implementation is compliant with the contract.
  2. moving TDD to an architecture level.

For more information please go to the Documentation

Requirements

Wiremock

In order to use Accurest with Wiremock you have to have Wiremock in version at least 2.0.0-beta . Of course the higher the better :)

Additional projects

Stub Runner

Allows you to download WireMock stubs from the provided Maven repository and runs them in WireMock servers.

Stub Runner JUnit

Stub Runner with JUnit rules

Stub Runner Spring

Spring Configuration that automatically starts stubs upon Spring Context build up

Stub Runner Spring Cloud

Spring Cloud AutoConfiguration that automatically starts stubs upon Spring Context build up and allows you to call the stubs as if they were registered in your service discovery

Maven project support with standalone Accurest Stub Runner and Accurest Contracts to Wiremock mappings converter

accurest's People

Contributors

cameleeck avatar dstepanov avatar gitter-badger avatar jkubrynski avatar kamilszymanski avatar marcingrzejszczak avatar mariuszs avatar mattreyuk avatar mfejzer avatar mzielinski avatar olgamaciaszek avatar rembol avatar smaragda avatar spencergibb avatar szpak avatar wszwech avatar yu55 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

accurest's Issues

Regex in status code doesn't work for Response

For Groovy DSL:

io.codearte.accurest.dsl.GroovyDsl.make {
                request {
                method """PUT"""
                url """/fraudcheck"""
                body("""
                    {
                    "clientPesel":"${value(client(regex('[0-9]{10}')), server('1234567890'))}",
                    "loanAmount":99999}
                """
                )
                headers {
                    header("""Content-Type""", """application/vnd.fraud.v1+json""")
                }

            }
            response {
                status $(client(200), server(regex('[0-9]{3}')))
                body( """{
    "fraudCheckStatus": "FRAUD",
    "rejectionReason": "Amount too high"
}""")
                headers {
                     header('Content-Type': 'application/vnd.fraud.v1+json')
                    }
            }

}

the created test doesn't recognize the Pattern and checks for equality for Response Status

def shouldMarkClientAsFraud() {
        given:
            def request = given()
                    .header('Content-Type', 'application/vnd.fraud.v1+json')
                    .body('{"clientPesel":"1234567890","loanAmount":99999}')

        when:
            def response = given().spec(request)
                    .put("/fraudcheck")

        then:
            response.statusCode == [0-9]{3}
            response.header('Content-Type') == 'application/vnd.fraud.v1+json'
        and:
            def responseBody = new JsonSlurper().parseText(response.body.asString())
            responseBody.fraudCheckStatus == "FRAUD"
            responseBody.rejectionReason == "Amount too high"
    }

Unable to use client and server for body JSON because of escaped quotes and new lines

I have this groovy DSL:

io.coderate.accurest.dsl.GroovyDsl.make {
    request {
        method 'POST'
        url '/api/validate'
        headers {
            header 'Content-Type': 'application/vnd.com.ofg.client-data-validation-gateway.v1+json'
        }
        body $(client(regex('''\
\\{
  "personalId": "[0-9]{11}",
  "firstName": "Test",
  "lastName": "Test",
  "birthDate": "1985-12-12"
\\}
''')), server('''\
{
  "personalId": "1234",
  "firstName": "Test",
  "lastName": "Test",
  "birthDate": "1985-12-12"
}
'''))
    }
    response {
        status 200
        body '''\
{
    "result": "VALID"
}
'''
    }
}

Server side quotes and new lines are escaped in the output like this:

.body('"{\n  \"personalId\": \"1234\",\n  \"firstName\": \"Test\",\n  \"lastName\": \"Test\",\n  \"birthDate\": \"1985-12-12\"\n}\n"')

I tried it with simple quotes without new lines with very similar result:

server('{"personalId": "1234", "firstName": "Test", "lastName": "Test", "birthDate": "1985-12-12"}')
.body('"{\"personalId\": \"1234\", \"firstName\": \"Test\", \"lastName\": \"Test\", \"birthDate\": \"1985-12-12\"}"')

Expected result:

 .body('{"birthDate":"1985-12-12","firstName":"Test","lastName":"Test","personalId":"1234"}')

GString request and 'execute' method fails

Having such a DSL:

io.codearte.accurest.dsl.GroovyDsl.make {
                request {
                    method """PUT"""
                    url """/fraudcheck"""
                    body("""
                        {
                        "clientPesel":"${value(client('1234567890'), server(execute('assertThatRejectionReasonIsNull($it)')))}",
                        "loanAmount":123.123
                        }
                    """
                    )
                    headers {
                        header("""Content-Type""", """application/vnd.fraud.v1+json""")

                    }

                }
            response {
                status 200
                body( """{
    "fraudCheckStatus": "OK",
    "rejectionReason": null
}""")
                headers {
                     header('Content-Type': 'application/vnd.fraud.v1+json')

                    }

            }

}

results in a following test:

def shouldMarkClientAsNotFraud() {
        given:
            def request = given()
                    .header('Content-Type', 'application/vnd.fraud.v1+json')
                    .body('{"clientPesel":"io.codearte.accurest.dsl.internal.ExecutionProperty@362cf66","loanAmount":123.123}')

        when:
            def response = given().spec(request)
                    .put("/fraudcheck")

        then:
            response.statusCode == 200
            response.header('Content-Type') == 'application/vnd.fraud.v1+json'
        and:
            def responseBody = new JsonSlurper().parseText(response.body.asString())
            responseBody.fraudCheckStatus == "OK"
            responseBody.rejectionReason == null
    }

Option to exclude files from generation

Currently there is no option to exclude files from test generation.
There should be a way to exclude both files and directories.
Additionally option to exclude using regexp pattern matching.

Having empty String breaks JSON parsing

given: JSON with an empty String (notice rejectionReason)

{
  "request": {
    "method": "PUT",
    "url": "/fraudcheck",
    "headers": {
      "Content-Type": {
        "equalTo": "application/vnd.frauddetectionservice.v1+json"
      }
    },
    "bodyPatterns": [
      {"matches": "\\{\"clientPesel\":\"123456789\",\"loanAmount\":100.5\\}"}
    ]
  },
  "response": {
    "status": 200,
    "body": "{\"fraudCheckStatus\":\"OK\",\"rejectionReason\":\"\"}",
    "headers": {
      "Content-Type": "application/vnd.frauddetectionservice.v1+json"
    }
  }
}

when: accurest generation is executed
then: exception is thrown

The assertion message is not descriptive

When providing client and server side with one side having a regexp and the other not matching it an assertion error is thrown without proper description.

For Groovy DSL

io.codearte.accurest.dsl.GroovyDsl.make {
                request {
                    method """PUT"""
                    url """/fraudcheck"""
                    body("""
                        {
                        "clientPesel":"${value(client(regex('[0-9]{10}')), server('12321312312312'))}",
                        "loanAmount":123.123
                        }
                    """
                    )
                    headers {
                        header("""Content-Type""", """application/vnd.fraud.v1+json""")

                    }

                }
            response {
                status 200
                body( """{
    "fraudCheckStatus": "OK",
    "rejectionReason": null
}""")
                headers {
                     header('Content-Type': 'application/vnd.fraud.v1+json')

                    }

            }

}

error message is:

Execution failed for task ':fraudDetectionService:generateWiremockClientStubs'.
> assert secondSide ==~ firstSide
         |          |   |
         |          |   [0-9]{10}
         |          false
         12321312312312

People might know what is secondSide and firstSide

Wrong property name for body in generated wiremock stubs

Currently, wiremock stubs are generated with the following structure for request body:

"body": {
    "equalTo": "{\"amount\":100}"
}

Wiremock expects bodyPatterns, and not body in the stub definition, so the generated structure should look like this:

"bodyPatterns": {
    "equalTo": "{\"amount\":100}"
}

Gradle plugin generates tests in wrong project

When running in daemon mode Gradle and using AccuREST in two different projects the plugin can generate tests in wrong project (in project the daemon was started in). Target directory used in generateAccurest should use project.buildDir not path path relative to working directory.

Add plugability of groovy dsl via SPI

Let's add possibility for people to add transformations to other things than Wiremock stubs or test code. There are other Http stub servers like Moco Mountback (don't remember if spelled it correctly) etc.

colon inside json (e.g. hal+json links)

I tried to use hal+json and there is an error in generated Spec. Links should be in quotes, becasue it contains colon. It shoul look like this: responseBody._links == [self:[href:'http://localhost:8095/clients/1']]. The output looks like this and is not valid: responseBody._links == [self:[href:http://localhost:8095/clients/1]]

{
"firstName" : "Test",
"lastName" : "Test",
"_links" : {
"self" : {
"href" : "http://localhost:8095/clients/1"
}
}
}

Can't execute custom logic on the server side

This will not work:

io.codearte.accurest.dsl.GroovyDsl.make {
    request {
        method 'PUT'
        url $(client(regex('^/api/[0-9]{2}$')), server('/api/12'))
        headers {
            header 'Content-Type': 'application/json'
        }
        body '''\
    [{
        "text": "Gonna see you at Warsaw"
    }]
'''
    }
    response {
        body (
             path: $(client('/api/12'), server(execute('customMethod($it)')))
        )
        status 200
    }
}

ExecutionProperty is not working in GStrings

For DSL

io.codearte.accurest.dsl.GroovyDsl.make {
                request {
                    method """PUT"""
                    url """/fraudcheck"""
                    body("""
                        {
                        "clientPesel":"${value(client(regex('[0-9]{10}')), server('1234567890'))}",
                        "loanAmount":123.123
                        }
                    """
                    )
                    headers {
                        header("""Content-Type""", """application/vnd.fraud.v1+json""")

                    }

                }
            response {
                status 200
                body( """{
    "fraudCheckStatus": "OK",
    "rejectionReason": ${value(client(null), server(execute('assertThatRejectionReasonIsNull($it)')))}
}""")
                headers {
                     header('Content-Type': 'application/vnd.fraud.v1+json')

                    }

            }

}

an exception is thrown while trying to generate Wiremock stubs

      at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.executeActions(ExecuteActionsTaskExecuter.java:61)
        ... 57 more
Caused by: groovy.json.JsonException: Unable to determine the current character, it is not a string, number, array, or object

The current character read is 'i' with an int value of 105
Unable to determine the current character, it is not a string, number, array, or object
line number 3
index number 55
    "rejectionReason": io.codearte.accurest.dsl.internal.ExecutionProperty@26d24d7a
.......................^
        at groovy.json.internal.JsonParserCharArray.decodeValueInternal(JsonParserCharArray.java:216)
        at groovy.json.internal.JsonParserCharArray.decodeJsonObject(JsonParserCharArray.java:140)
        at groovy.json.internal.JsonParserCharArray.decodeValueInternal(JsonParserCharArray.java:196)
        at groovy.json.internal.JsonParserCharArray.decodeValue(JsonParserCharArray.java:166)
        at groovy.json.internal.JsonParserCharArray.decodeFromChars(JsonParserCharArray.java:45)
        at groovy.json.internal.JsonParserCharArray.parse(JsonParserCharArray.java:409)
        at groovy.json.internal.BaseJsonParser.parse(BaseJsonParser.java:103)
        at groovy.json.JsonSlurper.parseText(JsonSlurper.java:208)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.lang.reflect.Method.invoke(Method.java:483)
        at org.codehaus.groovy.runtime.callsite.PojoMetaMethodSite$PojoCachedMethodSiteNoUnwrap.invoke(PojoMetaMethodSite.java:210)
        at org.codehaus.groovy.runtime.callsite.PojoMetaMethodSite.call(PojoMetaMethodSite.java:53)
        at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:116)
        at io.codearte.accurest.dsl.internal.Body.extractValue(Body.groovy:63)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.lang.reflect.Method.invoke(Method.java:483)
        at org.codehaus.groovy.reflection.CachedMethod.invoke(CachedMethod.java:90)
        at org.codehaus.groovy.runtime.callsite.StaticMetaMethodSite$StaticMetaMethodSiteNoUnwrapNoCoerce.invoke(StaticMetaMethodSite.java:148)
        at org.codehaus.groovy.runtime.callsite.StaticMetaMethodSite.callStatic(StaticMetaMethodSite.java:99)
        at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callStatic(AbstractCallSite.java:169)
        at io.codearte.accurest.dsl.internal.Body.<init>(Body.groovy:38)
        at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)

The problem is the same as with Pattern. I guess more hacks will have to be added to the io.codearte.accurest.dsl.internal.Body#extractValue(groovy.lang.GString, groovy.lang.Closure) ;)

Handling regular expression in server response body

It seems that accurest does not handle correctly regular expressions for the server side response. When I run compileTestGroovy for the following script:

io.codearte.accurest.dsl.GroovyDsl.make {
 request {
  method('PUT')
  url('/payment-order')

  body(
       [body]
  )
 }

 response {
  status 201
  body(
          correlationId: value(
                  client('500f0045-ceff-44b2-9ded-47de2ac55a51'),
                  server({ regex('[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}') }
                  )
          )
  )
  }
}

the following test code is generated:

class WorldpayAdapterSpec extends MvcSpec {

    def shouldRedirectToWorldpay() {
        given:
            [request definition]
        when:
            def response = given().spec(request)
                    .put("/payment-order")

        then:
            [...]
        responseBody.correlationId ==
    }
}

which of course does not compile.

I would expect responseBody.correlationId to be validated against the regexp provided in the DSL script.

Handling of optional parameters from request body when generating wiremock stubs

It is not currently possible to use the Groovy DSL for creating stub definitions with request bodies that accept optional parameters. It would be nice if something like this could be defined using the Groovy DSL:

${$(client(~/('loanNumber': '[0-9]+')?/), server('"loanNumber": 321'))}

This should then result in the following stub definition:

"bodyPatterns": [
    { "matches": "\\{(\"loanNumber\":\"[0-9]+\")?\\}" }
]

I guess nothing should have to change on the acceptance tests generation part.

What do you think? If you're ok with this idea, I could start working on a pull request in the following days.

GroovyDSL -> JSON - issue with new lines

    response {
        status 200
        body( """{
    "decision": "Continue Business Flow"
}""")
    }

is converted to:

    "response": {
        "body": "{\n    \"decision\": \"Continue Business Flow\"\n}",
    }

Documentation bug: typo

In the "Add gradle plugin" section, there should be "dependencies" instead of "dependecies".

Invalid null JSON parts processing

given: JSON with a null value (notice rejectionReason)

{
  "request": {
    "method": "PUT",
    "url": "/fraudcheck",
    "headers": {
      "Content-Type": {
        "equalTo": "application/vnd.frauddetectionservice.v1+json"
      }
    },
    "bodyPatterns": [
      {"matches": "\\{\"clientPesel\":\"123456789\",\"loanAmount\":100.5\\}"}
    ]
  },
  "response": {
    "status": 200,
    "body": "{\"fraudCheckStatus\":\"OK\",\"rejectionReason\":null}",
    "headers": {
      "Content-Type": "application/vnd.frauddetectionservice.v1+json"
    }
  }
}

when: accurest generation is executed
then: test has an invalid piece of code:

responseBody.rejectionReason=null == null

Response body with array generate invalid assertion in test

Version: 0.4.5

As you can see in below example test contains responseBody.null == null when body is an array

io.coderate.accurest.dsl.GroovyDsl.make {
    request {
        method """GET"""
        url """/admin/api/profiles"""
    }
    response {
        status 200
        body """[
{
    "property1": "a",
    "property2": "b"
}]"""
        headers {
            header('Content-Type': 'application/json')
        }
    }
}
def shouldValidateArrayRetrievedFromService() {
    given:
        def request = given()
    when:
        def response = given().spec(request)
                .get("/uri")
    then:
        response.statusCode == 200
        response.header('Content-Type') == 'application/json'
    and:
        def responseBody = new JsonSlurper().parseText(response.body.asString())
        responseBody.null == null
}

Introduce DSL that would be used to define stubs

Next this DSL would be transformed to wiremock. We should put it to a separate library

E.g.

Current approach

{
    "request": {
        "method": "GET",
        "urlPattern": "${/[0-9]{2}:/12}"
    },
    "response": {
        "status": 200,
        "body": "{\"date\":\"${\"2015-01-14\":$anyInt($it)}\"}}",
        "headers": {
            "Content-Type": "text/plain"
        }
    }
}

Approach to discuss:

request  {
    method('GET')
    urlPattern {
         client('$([0-9]{2}')
         server('12')
    }
}
response {
   status(200)
   body {
       withPlaceholder(client:'2015-01-14', server: '$anyInt($it)')
       withTemplate('''
        {
              "date" : "$placeholder1"
        }
       ''')
   }
   headers {
       Content-Type('text/plain')
   }
}

Wiremock equivalent for client:

{
    "request": {
        "method": "GET",
        "urlPattern": "[0-9]{2}"
    },
    "response": {
        "status": 200,
        "body": "2015-01-14",
        "headers": {
            "Content-Type": "text/plain"
        }
    }
}

Real support for JUnit

It is not a critical issue, but current support for JUnit is broken - Groovy code is generated in .java files which does not compile.

Task for validating contract dsl's

Because we can have separate fields for customer and server we should provide possibility to verify if there are no mistakes (running customer regex over server field). For example

url {
  customer matches "\/users\/[0-9]+$"
  server equalTo "/users/123"
}

will be validated positively while

url {
  customer matches "\/users\/[0-9]+$"
  server equalTo "/users/123a"
}

will fail

List of objects in response body miss quotes in output

version: 4.5

GroovyDsl:

body '''\
{
    "result": "NOT_VALID",
    "errors": [{"propertyName":"firstName", "providerValue":"Test"}]
}
'''

Output:

responseBody.errors == [[propertyName:firstName, providerValue:Test]]

workaround GroovyDsl:

"errors": [{"propertyName":"\"firstName\"", "providerValue":"\"Test\""}]

correct output:

responseBody.errors == [[propertyName:"firstName", providerValue:"Test"]]

Better error messages for invalid json file

:generateAccurest FAILED

FAILURE: Build failed with an exception.

* What went wrong:
Execution failed for task ':generateAccurest'.
> Cannot invoke method toLowerCase() on null object

in

at io.coderate.accurest.builder.SpockMethodBodyBuilder.appendTo(SpockMethodBodyBuilder.groovy:36)

for missing method field in request.

Stubs with Regexps are invalid Wiremock stubs

For Groovy DSL

io.codearte.accurest.dsl.GroovyDsl.make {
                request {
                method """PUT"""
                url """/fraudcheck"""
                body("""
                    {
                    "clientPesel":"${value(client(regex('[0-9]{10}')), server('1234567890'))}",
                    "loanAmount":99999}
                """
                )
                headers {
                    header("""Content-Type""", """application/vnd.fraud.v1+json""")
                }

            }
            response {
                status 200
                body( """{
    "fraudCheckStatus": "${value(client('FRAUD'), server(regex('[A-Z]{5}')))}",
    "rejectionReason": "Amount too high"
}""")
                headers {
                     header('Content-Type': 'application/vnd.fraud.v1+json')
                    }
            }

}

and such Wiremock stub

{
    "request": {
        "method": "PUT",
        "headers": {
            "Content-Type": {
                "equalTo": "application/vnd.fraud.v1+json"
            }
        },
        "url": "/fraudcheck",
        "bodyPatterns": [
            {
                "matches": "{\"clientPesel\":\"[0-9]{10}\",\"loanAmount\":\"99999\"}"
            }
        ]
    },
    "response": {
        "status": 200,
        "headers": {
            "Content-Type": "application/vnd.fraud.v1+json"
        },
        "body": "{\"fraudCheckStatus\":\"FRAUD\",\"rejectionReason\":\"Amount too high\"}"
    }
}

Exception is thrown

org.springframework.web.client.HttpServerErrorException: 500 Illegal repetition {"clientPesel":"[0-9]{10}","loanAmount":"123.123"}
    at org.springframework.web.client.DefaultResponseErrorHandler.handleError(DefaultResponseErrorHandler.java:94)
    at org.springframework.web.client.RestTemplate.handleResponseError(RestTemplate.java:615)
    at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:573)
    at org.springframework.web.client.RestTemplate.execute(RestTemplate.java:529)
    at org.springframework.web.client.RestTemplate.exchange(RestTemplate.java:447)
    at com.blogspot.toomuchcoding.frauddetection.LoanApplicationService.sendRequestToFraudDetectionService(LoanApplicationService.java:44)
    at com.blogspot.toomuchcoding.frauddetection.LoanApplicationService.loanApplication(LoanApplicationService.java:33)
    at com.blogspot.toomuchcoding.LoanApplicationServiceSpec.should be rejected due to abnormal loan amount(LoanApplicationServiceSpec.groovy:43)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.