Compare commits
1 commit
main
...
graphql_cl
Author | SHA1 | Date | |
---|---|---|---|
|
ee1fd9915a |
53 changed files with 996 additions and 591 deletions
|
@ -1,14 +1,15 @@
|
|||
package com.surrus.peopleinspace
|
||||
|
||||
import com.surrus.common.remote.Assignment
|
||||
import com.surrus.common.remote.IssPosition
|
||||
import com.surrus.common.model.Assignment
|
||||
import com.surrus.common.model.IssPosition
|
||||
import com.surrus.common.repository.PeopleInSpaceRepositoryInterface
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.flowOf
|
||||
|
||||
class PeopleInSpaceRepositoryFake: PeopleInSpaceRepositoryInterface {
|
||||
val peopleList = listOf(Assignment("Apollo 11", "Neil Armstrong"),
|
||||
Assignment("Apollo 11", "Buzz Aldrin"))
|
||||
Assignment("Apollo 11", "Buzz Aldrin")
|
||||
)
|
||||
|
||||
val issPosition = IssPosition(53.2743394, -9.0514163)
|
||||
|
||||
|
|
|
@ -16,7 +16,7 @@ import androidx.glance.layout.padding
|
|||
import androidx.glance.text.FontWeight
|
||||
import androidx.glance.text.TextStyle
|
||||
import androidx.glance.unit.ColorProvider
|
||||
import com.surrus.common.remote.Assignment
|
||||
import com.surrus.common.model.Assignment
|
||||
import com.surrus.common.repository.PeopleInSpaceRepositoryInterface
|
||||
import com.surrus.peopleinspace.glance.util.BaseGlanceAppWidget
|
||||
import kotlinx.coroutines.flow.first
|
||||
|
|
|
@ -17,7 +17,7 @@ import androidx.compose.ui.semantics.SemanticsPropertyKey
|
|||
import androidx.compose.ui.semantics.SemanticsPropertyReceiver
|
||||
import androidx.compose.ui.semantics.semantics
|
||||
import androidx.compose.ui.viewinterop.AndroidView
|
||||
import com.surrus.common.remote.IssPosition
|
||||
import com.surrus.common.model.IssPosition
|
||||
import com.surrus.peopleinspace.util.collectAsStateWithLifecycle
|
||||
import org.koin.androidx.compose.getViewModel
|
||||
import org.osmdroid.util.GeoPoint
|
||||
|
|
|
@ -22,7 +22,7 @@ import androidx.navigation.compose.currentBackStackEntryAsState
|
|||
import com.google.accompanist.navigation.animation.AnimatedNavHost
|
||||
import com.google.accompanist.navigation.animation.composable
|
||||
import com.google.accompanist.navigation.animation.rememberAnimatedNavController
|
||||
import com.surrus.common.remote.Assignment
|
||||
import com.surrus.common.model.Assignment
|
||||
|
||||
class MainActivity : ComponentActivity() {
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
|
|
|
@ -2,7 +2,7 @@ package com.surrus.peopleinspace.ui
|
|||
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.surrus.common.remote.Assignment
|
||||
import com.surrus.common.model.Assignment
|
||||
import com.surrus.common.repository.PeopleInSpaceRepositoryInterface
|
||||
import kotlinx.coroutines.flow.SharingStarted
|
||||
import kotlinx.coroutines.flow.stateIn
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
package com.surrus.peopleinspace.ui
|
||||
|
||||
import androidx.compose.ui.tooling.preview.datasource.CollectionPreviewParameterProvider
|
||||
import com.surrus.common.remote.Assignment
|
||||
import com.surrus.common.model.Assignment
|
||||
|
||||
class PersonProvider : CollectionPreviewParameterProvider<Assignment>(
|
||||
listOf(
|
||||
|
|
|
@ -18,15 +18,15 @@ import androidx.compose.ui.text.TextStyle
|
|||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import coil.compose.rememberImagePainter
|
||||
import com.surrus.common.remote.Assignment
|
||||
import com.surrus.common.model.Assignment
|
||||
import org.koin.androidx.compose.getViewModel
|
||||
|
||||
const val PersonListTag = "PersonList"
|
||||
|
||||
@Composable
|
||||
fun PersonListScreen(paddingValues: PaddingValues = PaddingValues(),
|
||||
personSelected: (person: Assignment) -> Unit,
|
||||
peopleInSpaceViewModel: PeopleInSpaceViewModel = getViewModel()
|
||||
personSelected: (person: Assignment) -> Unit,
|
||||
peopleInSpaceViewModel: PeopleInSpaceViewModel = getViewModel()
|
||||
) {
|
||||
val peopleState = peopleInSpaceViewModel.peopleInSpace.collectAsState()
|
||||
|
||||
|
|
1
backend/.gitignore
vendored
1
backend/.gitignore
vendored
|
@ -1 +0,0 @@
|
|||
/build
|
|
@ -1,37 +0,0 @@
|
|||
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
|
||||
|
||||
plugins {
|
||||
id("kotlin-platform-jvm")
|
||||
application
|
||||
kotlin("plugin.serialization")
|
||||
id("com.github.johnrengelman.shadow")
|
||||
}
|
||||
|
||||
dependencies {
|
||||
with(Deps.Kotlinx) {
|
||||
implementation(serializationCore) // JVM dependency
|
||||
implementation(coroutinesCore)
|
||||
}
|
||||
|
||||
with(Deps.Ktor) {
|
||||
implementation(serverCore)
|
||||
implementation(serverNetty)
|
||||
implementation(websockets)
|
||||
implementation(serverContentNegotiation)
|
||||
implementation(json)
|
||||
}
|
||||
|
||||
with(Deps.Log) {
|
||||
implementation(logback)
|
||||
}
|
||||
|
||||
implementation(project(":common"))
|
||||
}
|
||||
|
||||
tasks.withType<KotlinCompile> {
|
||||
kotlinOptions.jvmTarget = "1.8"
|
||||
}
|
||||
|
||||
application {
|
||||
mainClass.set("ServerKt")
|
||||
}
|
|
@ -1,2 +0,0 @@
|
|||
runtime: java11
|
||||
entrypoint: java -Xmx64m -jar server.jar
|
|
@ -1,80 +0,0 @@
|
|||
val personImages = mapOf(
|
||||
"Chris Cassidy" to "https://www.nasa.gov/sites/default/files/styles/side_image/public/thumbnails/image/9368855148_f79942efb7_o.jpg?itok=-w5yoryN",
|
||||
"Anatoly Ivanishin" to "https://upload.wikimedia.org/wikipedia/commons/thumb/e/e0/Anatoli_Ivanishin_2011.jpg/440px-Anatoli_Ivanishin_2011.jpg",
|
||||
"Ivan Vagner" to "http://www.spacefacts.de/more/cosmonauts/photo/vagner_ivan_3.jpg",
|
||||
"Sergey Ryzhikov" to "https://spaceflight101.com/iss-expedition-50/wp-content/uploads/sites/118/2016/11/jsc2016e105228.jpg",
|
||||
"Kate Rubins" to "https://spaceflight101.com/iss-expedition-49/wp-content/uploads/sites/110/2016/09/26720141242_be992e9a20_o-768x1152.jpg",
|
||||
"Sergey Kud-Sverchkov" to "https://www.esa.int/var/esa/storage/images/esa_multimedia/images/2014/08/sergey_kud-sverchkov/14716838-1-eng-GB/Sergey_Kud-Sverchkov_pillars.jpg",
|
||||
"Mike Hopkins" to "https://pbs.twimg.com/media/Em5EbQOVEAAdZ0h?format=jpg&name=medium",
|
||||
"Victor Glover" to "https://pbs.twimg.com/media/Em5EbSnUYAEAgyl?format=jpg&name=medium",
|
||||
"Shannon Walker" to "https://pbs.twimg.com/media/Em5EbQPVoAATIx8?format=jpg&name=medium",
|
||||
"Soichi Noguchi" to "https://pbs.twimg.com/media/Em5EbSoVcAA3R2F?format=jpg&name=medium",
|
||||
"Mark Vande Hei" to "https://www.esa.int/var/esa/storage/images/esa_multimedia/images/2016/09/mark_vande_hei/16121862-1-eng-GB/Mark_Vande_Hei_pillars.jpg",
|
||||
"Oleg Novitskiy" to "https://spaceflight101.com/iss-expedition-50/wp-content/uploads/sites/118/2016/11/jsc2016e165868.jpg",
|
||||
"Pyotr Dubrov" to "https://www.nasa.gov/sites/default/files/styles/full_width_feature/public/thumbnails/image/jsc2021e010288.jpg",
|
||||
"Shane Kimbrough" to "https://www.nasa.gov/sites/default/files/styles/full_width_feature/public/thumbnails/image/jsc2021e010824.jpg",
|
||||
"Megan McArthur" to "https://www.nasa.gov/sites/default/files/styles/full_width_feature/public/thumbnails/image/jsc2021e010823.jpg",
|
||||
"Akihiko Hoshide" to "https://www.nasa.gov/sites/default/files/styles/full_width_feature/public/thumbnails/image/jsc2021e010825.jpg",
|
||||
"Thomas Pesquet" to "https://www.nasa.gov/sites/default/files/styles/full_width_feature/public/thumbnails/image/jsc2021e010826.jpg",
|
||||
"Nie Haisheng" to "http://www.spacefacts.de/more/taikonauts/photo/nie_haisheng_1.jpg",
|
||||
"Liu Boming" to "http://www.april12.eu/chinaastron/photo/liuboming.jpg",
|
||||
"Tang Hongbo" to "https://upload.wikimedia.org/wikipedia/commons/3/35/Tang_Hongbo.png",
|
||||
"Chris Sembroski" to "https://assets.newatlas.com/dims4/default/9fd3579/2147483647/strip/true/crop/828x1037+0+0/resize/767x960!/quality/90/?url=http%3A%2F%2Fnewatlas-brightspot.s3.amazonaws.com%2F20%2F75%2F205f2e594adf9492c0c0275e2ec6%2Fi4-chris-s-129a9147.jpg",
|
||||
"Hayley Arceneaux" to "https://pbs.twimg.com/media/E_RKiXIXEAECmiP.jpg",
|
||||
"Sian Procto" to "https://pbs.twimg.com/media/E_VcnA2XsAgp4r-.jpg",
|
||||
"Jared Isaacman" to "https://pbs.twimg.com/media/E_OTuR-XsAEhsln.jpg",
|
||||
"Anton Shkaplerov" to "https://alchetron.com/cdn/anton-shkaplerov-799e7545-54ae-4145-83d1-18f906d22b1-resize-750.jpeg",
|
||||
"Klim Shipenko" to "https://upload.wikimedia.org/wikipedia/pt/f/f9/Shipenko_klim.jpg",
|
||||
"Yulia Pereslid" to "https://metro.co.uk/wp-content/uploads/2021/10/PRI_203475964.jpg?quality=90&strip=all&zoom=1&resize=540%2C810",
|
||||
"Zhai Zhigang" to "https://dingyue.ws.126.net/2021/0701/aaa7e1e7j00qvkk13001hc000hs00lzg.jpg",
|
||||
"Wang Yaping" to "https://alchetron.com/cdn/wang-yaping-2f869150-f66f-4884-b638-62ff678084a-resize-750.jpeg",
|
||||
"Ye Guangfu" to "https://upload.wikimedia.org/wikipedia/commons/a/a2/Ye_Guangfu_in_2021.jpg",
|
||||
"Raja Chari" to "https://www.spacex.com/static/images/crew-3/portraits/SPACEX_Crew-3_RajaChari.jpg",
|
||||
"Tom Marshburn" to "https://www.spacex.com/static/images/crew-3/portraits/SPACEX_Crew-3_TomMarshburn.jpg",
|
||||
"Kayla Barron" to "https://www.spacex.com/static/images/crew-3/portraits/SPACEX_Crew-3_KaylaBarron.jpg",
|
||||
"Matthias Maurer" to "https://www.spacex.com/static/images/crew-3/portraits/SPACEX_Crew-3_MathiasMaurer.jpg",
|
||||
"Alexander Misurkin" to "https://spaceflight101.com/iss/wp-content/uploads/sites/37/2017/08/36468940815_70ef48a8b6_o-768x1152.jpg",
|
||||
"Yusaku Maezawa" to "https://pbs.twimg.com/media/FD599ihaUAAyjC7?format=jpg",
|
||||
"Yozo Hirano" to "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcS1lYvHXPvl-eG3yX5MWAtvTaxkoiISJ5KXrg&usqp=CAU",
|
||||
)
|
||||
|
||||
val personBios = mapOf(
|
||||
"Chris Cassidy" to "Christopher John \"Chris\" Cassidy (born January 4, 1970, in Salem, Massachusetts) is a NASA astronaut and United States Navy SEAL. Chris Cassidy achieved the rank of captain in the U.S. Navy. He was the Chief of the Astronaut Office at NASA from July 2015 until June 2017.",
|
||||
"Anatoly Ivanishin" to "Anatoli Alekseyevich Ivanishin (Russian: Анатолий Алексеевич Иванишин; born 15 January 1969) is a Russian cosmonaut. His first visit to space was to the International Space Station on board the Soyuz TMA-22 spacecraft as an Expedition 29 / Expedition 30 crew member, launching in November 2011 and returning in April 2012. Ivanishin was the Commander of the International Space Station for Expedition 49.",
|
||||
"Ivan Vagner" to "Ivan Viktorovich Vagner (born 10 July 1985) is a Russian engineer and cosmonaut who was selected in October 2010. He graduated from the Baltic State Technical University in 2008, before working as an engineer for RKK Energia.\n\nHe began his first spaceflight in April 2020 as a Flight Engineer on Soyuz MS-16 and Expedition 62/63.",
|
||||
"Sergey Ryzhikov" to "Sergey Nikolayevich Ryzhikov (Russian: Сергей Николаевич Рыжиков; born on August 19, 1974), lieutenant colonel of Russian Air Force, is a Russian cosmonaut, selected in 2006. Ryzhikov launched on his first spaceflight on board the Soyuz MS-02 spacecraft. He spent approximately six months on board the International Space Station taking part in Expedition 49/50, returning to Earth on April 10, 2017",
|
||||
"Kate Rubins" to "Kathleen Hallisey \"Kate\" Rubins (born October 14, 1978) is a NASA astronaut. She became the 60th woman to fly in space when she launched on a Soyuz spacecraft to the International Space Station on July 7, 2016. She returned to Earth on October 30, 2016 aboard a Soyuz. She was a crew member of Expedition 48 and Expedition 49 of the International Space Station.",
|
||||
"Sergey Kud-Sverchkov" to "Sergey Vladimirovich Kud-Sverchkov was born on August 23, 1983 at the Baikonur Cosmodrome in the Kazakh Soviet Socialist Republic. Sergey Kud-Sverchkov is married and father of one daughter. Since April 2010, he is a Russian Cosmonaut of the Russian Space Agency Roscosmos. He is currently in space.",
|
||||
"Mike Hopkins" to "Michael Scott Hopkins was born on December 28, 1968 in Lebanon, Missouri but grew up on a farm in Richland, Missouri in a United Methodist family. After graduating from the School of the Osage High School in Lake of the Ozarks, Missouri, in 1987, he entered the University of Illinois at Urbana-Champaign. While there, he played defensive back for the Illinois Fighting Illini football team. He graduated in 1991 with a Bachelor of Science degree in aerospace engineering. He followed his undergraduate studies with a Master of Science degree in aerospace engineering from Stanford University, which he earned in 1992.",
|
||||
"Victor Glover" to "Victor Jerome Glover (born April 30, 1976) is a NASA astronaut of the class of 2013 and Pilot on the first operational flight of the SpaceX Crew Dragon to the International Space Station. Glover is a commander in the U.S. Navy where he pilots an F/A-18, and a graduate of the U.S. Air Force Test Pilot School.",
|
||||
"Shannon Walker" to "Shannon Walker (born 4 June 1965 in Houston, Texas) is an American physicist and a NASA astronaut selected in 2004. She launched on her first mission into space on 25 June 2010 onboard Soyuz TMA-19 and spent over 163 days in space.\n\nShe returned to space for her second long duration mission on 15 November 2020, onboard SpaceX Crew-1, the first operational flight of SpaceX's Crew Dragon spacecraft.",
|
||||
"Soichi Noguchi" to "Soichi Noguchi (野口 聡一, Noguchi Sōichi, born 15 April 1965 in Yokohama, Japan) is a Japanese aeronautical engineer and JAXA astronaut. His first spaceflight was as a Mission Specialist aboard STS-114 on 26 July 2005 for NASA's first \"return to flight\" Space Shuttle mission after the Columbia disaster. He was also in space as part of the Soyuz TMA-17 crew and Expedition 22 to the International Space Station (ISS), returning to Earth on 2 June 2010. He is the fifth Japanese astronaut to fly in space and the fourth to fly on the space shuttle. His third flight is onboard the Dragon 2 capsule for the SpaceX Crew-1 mission which launched successfully on November 15, 2020. This makes him one of only three astronauts to fly on three different launch systems.",
|
||||
"Mark Vande Hei" to "Mark Thomas Vande Hei (born November 10, 1966) is a retired United States Army officer and NASA astronaut who served as a flight Engineer for Expedition 53 and 54 on the International Space Station.",
|
||||
"Oleg Novitskiy" to "Oleg Viktorovich Novitskiy (Russian: Олег Викторович Новицкий; born October 12, 1971 in Červień, Belarus) is a former Lieutenant Colonel in the Russian Air Force who logged over 700 hours of flight time and was awarded for bravery. He is currently serving as a Russian cosmonaut with Roskosmos and has participated in multiple expeditions, during which he has spent over 340 days in space.",
|
||||
"Pyotr Dubrov" to "Pyotr Valerievich Dubrov (Russian: Пётр Валерьевич Дубров; born 30 January 1978) is a Russian engineer and cosmonaut selected by Roscosmos in 2012.",
|
||||
"Shane Kimbrough" to "Robert Shane Kimbrough (born June 4, 1967) is a retired United States Army officer, and a NASA astronaut. He was part of the first group of candidates selected for NASA astronaut training following the Space Shuttle Columbia disaster. Kimbrough is a veteran of two spaceflights, the first being a Space Shuttle flight, and the second being a six-month mission to the ISS on board a Russian Soyuz craft. He was the commander of the International Space Station for Expedition 50, and returned to Earth in April 2017.",
|
||||
"Megan McArthur" to "Katherine Megan McArthur (born August 30, 1971) is an American oceanographer, engineer, and a National Aeronautics and Space Administration (NASA) astronaut. She has served as a Capsule Communicator (CAPCOM) for both the space shuttle and space station. Megan McArthur has flown one space shuttle mission, STS-125. She is known as the last person to be hands on with the Hubble Space Telescope via the Canadarm. McArthur has served in a number of positions including working in the Shuttle Avionics Laboratory (SAIL).",
|
||||
"Akihiko Hoshide" to "Akihiko Hoshide (星出 彰彦, Hoshide Akihiko, born December 28, 1968) is a Japanese engineer and JAXA astronaut. On August 30, 2012, Hoshide became the third Japanese astronaut to walk in space.",
|
||||
"Thomas Pesquet" to "Thomas Gautier Pesquet (French pronunciation: \u200B[tɔma gotje pɛskɛ]; born 27 February 1978 in Rouen) is a French aerospace engineer, pilot, and European Space Agency astronaut. Pesquet was selected by ESA as a candidate in May 2009, and he successfully completed his basic training in November 2010. From November 2016 to June 2017, Pesquet was part of Expedition 50 and Expedition 51 as a flight engineer.Pesquet returned to space in April 2021 on board the SpaceX Crew Dragon for a second six-month stay on the ISS.",
|
||||
"Nie Haisheng" to "Nie Haisheng (simplified Chinese: 聂海胜; traditional Chinese: 聶海勝; pinyin: Niè Hǎishèng; born 13 October 1964) is a Chinese military pilot and CNSA astronaut.",
|
||||
"Liu Boming" to "Liu Boming (simplified Chinese: 刘伯明; traditional Chinese: 劉伯明; pinyin: Liú Bómíng; born September 1966) is a Chinese pilot selected as part of the Shenzhou program. A fighter pilot in the People's Liberation Army Air Force, he was selected to be an CNSA member in 1998.",
|
||||
"Tang Hongbo" to "Tang Hongbo (Chinese: 汤洪波; born October 1975) is a Chinese pilot selected as part of the Shenzhou program.",
|
||||
"Chris Sembroski" to "Christopher Sembroski (born August 28, 1979) is an American data engineer, Air Force veteran, and commercial astronaut, currently living in Everett, Washington, United States. He is a Lockheed Martin employee and private astronaut for the Inspiration4 mission.The position was given to Sembroski after a friend had declined the prize, transferring it to Sembroski.Sembroski has long had an interest in space, being an amateur astronomer and rocketeer.",
|
||||
"Hayley Arceneaux" to "Hayley Arceneaux is a St. Jude Children's Research Hospital employee, bone cancer survivor and private astronaut who is now a physician assistant; she joined billionaire Jared Isaacman on SpaceX's first private spaceflight Inspiration4 launched on September 15, 2021. At age 29, Arceneaux became the youngest American in space.",
|
||||
"Sian Procto" to "Sian Hayley Proctor is an American geology professor, science communicator, and commercial astronaut. She was selected as the pilot for the Inspiration4 private orbital spaceflight conducted on 15th September 2021, aboard a SpaceX-operated Crew Dragon space capsule.She is a geology professor at South Mountain Community College in Arizona.She is also a major in the Civil Air Patrol where she serves as the aerospace education officer for its Arizona Wing.",
|
||||
"Jared Isaacman" to "Jared Isaacman (born February 11, 1983) is an American billionaire businessman, pilot and amateur astronaut. He is the founder and CEO of Shift4 Payments, a payment processor. Isaacman served as commander of the SpaceX flight Inspiration4, launched September 15, 2021",
|
||||
"Anton Shkaplerov" to "Anton Nikolaevich Shkaplerov (Russian: Антон Николаевич Шкаплеров; born 20 February 1972) is a Russian cosmonaut. He is a veteran of four spaceflights and is a former Commander of the International Space Station.",
|
||||
"Klim Shipenko" to "Klim Alekseevich Shipenko (Russian: Клим Алексеевич Шипенко; born 16 June 1983) is a Russian film director, screenwriter, actor and producer. In 2021, Shipenko is planning to shoot portions of a science fiction film aboard the International Space Station. It is to be the second narrative film shot in space, and first feature film shot in space.",
|
||||
"Yulia Pereslid" to "Yulia Sergeevna Peresild (Russian: Ю́лия Серге́евна Переси́льд; born 5 September 1984) is a Russian stage and film actress. ",
|
||||
"Zhai Zhigang" to "Zhai Zhigang (born October 10, 1966) is a major general of the People's Liberation Army Strategic Support Force (PLASSF) in active service as a People's Liberation Army Astronaut Corps (PLAAC) taikonaut. During the Shenzhou 7 mission in 2008, he became the first Chinese citizen to carry out a spacewalk. He was a People's Liberation Army Air Force (PLAAF) fighter pilot.",
|
||||
"Wang Yaping" to "Colonel Wang Yaping (born 27 January 1980) is a Chinese military pilot and astronaut. Wang was the second female astronaut selected to the People's Liberation Army Astronaut Corps, and the second Chinese woman in space.",
|
||||
"Ye Guangfu" to "Colonel Ye Guangfu (Chinese: 叶光富; born 1 September 1980[1]) is a Chinese People's Liberation Army Astronaut Corps (PLAAC) astronaut selected as part of the Shenzhou program.",
|
||||
"Raja Chari" to "Raja Jon Vurputoor \"Grinder\" Chari (born June 24, 1977; Colonel, United States Air Force) is an American test pilot and NASA astronaut. He is a graduate of the U.S. Air Force Academy, Massachusetts Institute of Technology, and U.S. Naval Test Pilot School, and has over 2,000 flying hours.",
|
||||
"Tom Marshburn" to "Thomas Henry \"Tom\" Marshburn (born August 29, 1960) is an American physician and a NASA astronaut. He is a veteran of two spaceflights to the International Space Station.",
|
||||
"Kayla Barron" to "Kayla Jane Barron (born September 19, 1987; LCDR, USN) is an American submarine warfare officer, engineer and NASA astronaut.",
|
||||
"Matthias Maurer" to "Matthias Josef Maurer (born 18 March 1970 in St. Wendel, Saarland) is a German European Space Agency astronaut and materials scientist, who was selected in 2015 to take part in space training.",
|
||||
"Alexander Misurkin" to "Alexander Alexanderovich Misurkin (Russian: Aлександр Aлександрович Мисуркин) (born September 23, 1977), a major in the Russian Air Force, is a Russian cosmonaut, selected in 2006. He flew aboard Soyuz TMA-08M on 28 March 2013 as his first space mission, and launched on Soyuz MS-06 as his second flight, in 2017. He was Commander of the International Space Station for Expedition 54.",
|
||||
"Yusaku Maezawa" to "Yusaku Maezawa (前澤 友作, Maezawa Yūsaku, born 22 November 1975) is a Japanese billionaire entrepreneur and art collector. He founded Start Today in 1998 and launched the online fashion retail website Zozotown in 2004, now Japan's largest. Most recently, Maezawa introduced a custom-fit apparel brand ZOZO and at-home measurement system, the ZOZOSUIT, in 2018. As of July 2021, he is estimated by Forbes to have a net worth of $1.9 billion.",
|
||||
"Yozo Hirano" to "Yozo Hirano (Japanese: 平野 陽三, 1985- ) born in Imabari, Ehime Prefecture, Japan, is a Japanese spaceflight participant. He is scheduled to fly on Soyuz MS-20.\n\n"
|
||||
+ "He is foreseen to fly with Yusaku Maezawa, who will pay for both seats; his function will be as production assistant of Maezawa and to document the flight."
|
||||
)
|
|
@ -1,54 +0,0 @@
|
|||
import com.surrus.common.di.initKoin
|
||||
import com.surrus.common.remote.Assignment
|
||||
import com.surrus.common.remote.AstroResult
|
||||
import com.surrus.common.remote.PeopleInSpaceApi
|
||||
import io.ktor.server.application.*
|
||||
import io.ktor.serialization.kotlinx.json.*
|
||||
import io.ktor.server.engine.*
|
||||
import io.ktor.server.netty.*
|
||||
import io.ktor.server.plugins.*
|
||||
import io.ktor.server.response.*
|
||||
import io.ktor.server.routing.*
|
||||
|
||||
fun main() {
|
||||
val koin = initKoin(enableNetworkLogs = true).koin
|
||||
val peopleInSpaceApi = koin.get<PeopleInSpaceApi>()
|
||||
peopleInSpaceApi.baseUrl = "http://api.open-notify.org"
|
||||
|
||||
val port = System.getenv().getOrDefault("PORT", "8080").toInt()
|
||||
embeddedServer(Netty, port) {
|
||||
install(ContentNegotiation) {
|
||||
json()
|
||||
}
|
||||
|
||||
routing {
|
||||
|
||||
get("/astros.json") {
|
||||
val ar = peopleInSpaceApi.fetchPeople()
|
||||
val result = AstroResult(ar.message, ar.number, ar.people.map {
|
||||
val personImageUrl = personImages[it.name]
|
||||
val personBio = personBios[it.name]
|
||||
Assignment(it.craft, it.name, personImageUrl, personBio)
|
||||
})
|
||||
call.respond(result)
|
||||
}
|
||||
|
||||
get("/iss-now.json") {
|
||||
val result = peopleInSpaceApi.fetchISSPosition()
|
||||
call.respond(result)
|
||||
}
|
||||
|
||||
get("/astros_local.json") {
|
||||
val result = AstroResult(
|
||||
"success", 3,
|
||||
listOf(
|
||||
Assignment("ISS", "Chris Cassidy"),
|
||||
Assignment("ISS", "Anatoly Ivanishin"),
|
||||
Assignment("ISS", "Ivan Vagner")
|
||||
)
|
||||
)
|
||||
call.respond(result)
|
||||
}
|
||||
}
|
||||
}.start(wait = true)
|
||||
}
|
|
@ -11,16 +11,16 @@ buildscript {
|
|||
|
||||
dependencies {
|
||||
// keeping this here to allow AS to automatically update
|
||||
classpath("com.android.tools.build:gradle:7.0.4")
|
||||
classpath("com.android.tools.build:gradle:7.1.0")
|
||||
classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:${kotlinVersion}")
|
||||
classpath("org.jetbrains.kotlin:kotlin-serialization:${kotlinVersion}")
|
||||
|
||||
with(Deps.Gradle) {
|
||||
classpath(sqlDelight)
|
||||
classpath(shadow)
|
||||
classpath(kotlinter)
|
||||
classpath(gradleVersionsPlugin)
|
||||
classpath("com.rickclephas.kmp:kmp-nativecoroutines-gradle-plugin:${Versions.kmpNativeCoroutinesVersion}")
|
||||
classpath("com.apollographql.apollo3:apollo-gradle-plugin:${Versions.apollo}")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,10 +5,11 @@ object Versions {
|
|||
|
||||
const val kotlinCoroutines = "1.6.0"
|
||||
const val koin = "3.1.4"
|
||||
const val ktor = "2.0.0-beta-1"
|
||||
const val kotlinxSerialization = "1.3.2"
|
||||
const val kotlinxHtmlJs = "0.7.3"
|
||||
|
||||
const val apollo = "3.0.0"
|
||||
|
||||
const val kmpNativeCoroutinesVersion = "0.11.1-new-mm"
|
||||
|
||||
const val compose = "1.1.0-rc01"
|
||||
|
@ -25,7 +26,6 @@ object Versions {
|
|||
const val mockito = "3.11.2"
|
||||
const val robolectric = "4.6.1"
|
||||
|
||||
const val sqlDelight = "1.5.3"
|
||||
const val shadow = "7.0.0"
|
||||
const val kotlinterGradle = "3.4.5"
|
||||
|
||||
|
@ -52,7 +52,6 @@ object Deps {
|
|||
object Gradle {
|
||||
const val kotlinter = "org.jmailen.gradle:kotlinter-gradle:${Versions.kotlinterGradle}"
|
||||
const val shadow = "gradle.plugin.com.github.jengelman.gradle.plugins:shadow:${Versions.shadow}"
|
||||
const val sqlDelight = "com.squareup.sqldelight:gradle-plugin:${Versions.sqlDelight}"
|
||||
const val gradleVersionsPlugin = "com.github.ben-manes:gradle-versions-plugin:${Versions.gradleVersionsPlugin}"
|
||||
}
|
||||
|
||||
|
@ -74,6 +73,14 @@ object Deps {
|
|||
const val activityCompose = "androidx.activity:activity-compose:${Versions.activityCompose}"
|
||||
}
|
||||
|
||||
object Apollo {
|
||||
const val apolloRuntime = "com.apollographql.apollo3:apollo-runtime:${Versions.apollo}"
|
||||
const val apolloNormalizedCacheInMemory = "com.apollographql.apollo3:apollo-normalized-cache:${Versions.apollo}"
|
||||
const val apolloNormalizedCacheSqlite = "com.apollographql.apollo3:apollo-normalized-cache-sqlite:${Versions.apollo}"
|
||||
const val apolloMockServer = "com.apollographql.apollo3:apollo-mockserver:${Versions.apollo}"
|
||||
const val apolloTestingSupport = "com.apollographql.apollo3:apollo-testing-support:${Versions.apollo}"
|
||||
}
|
||||
|
||||
object Test {
|
||||
const val junit = "junit:junit:${Versions.junit}"
|
||||
const val androidXTestJUnit = "androidx.test.ext:junit:${Versions.androidXTestJUnit}"
|
||||
|
@ -111,34 +118,6 @@ object Deps {
|
|||
const val compose = "io.insert-koin:koin-androidx-compose:${Versions.koin}"
|
||||
}
|
||||
|
||||
object Ktor {
|
||||
const val serverCore = "io.ktor:ktor-server-core:${Versions.ktor}"
|
||||
const val serverNetty = "io.ktor:ktor-server-netty:${Versions.ktor}"
|
||||
const val contentNegotiation = "io.ktor:ktor-client-content-negotiation:${Versions.ktor}"
|
||||
const val json = "io.ktor:ktor-serialization-kotlinx-json:${Versions.ktor}"
|
||||
|
||||
const val serverContentNegotiation = "io.ktor:ktor-server-content-negotiation:${Versions.ktor}"
|
||||
|
||||
const val websockets = "io.ktor:ktor-websockets:${Versions.ktor}"
|
||||
const val clientCore = "io.ktor:ktor-client-core:${Versions.ktor}"
|
||||
const val clientJson = "io.ktor:ktor-client-json:${Versions.ktor}"
|
||||
const val clientLogging = "io.ktor:ktor-client-logging:${Versions.ktor}"
|
||||
const val clientSerialization = "io.ktor:ktor-client-serialization:${Versions.ktor}"
|
||||
const val clientAndroid = "io.ktor:ktor-client-android:${Versions.ktor}"
|
||||
const val clientJava = "io.ktor:ktor-client-java:${Versions.ktor}"
|
||||
const val clientIos = "io.ktor:ktor-client-ios:${Versions.ktor}"
|
||||
const val clientJs = "io.ktor:ktor-client-js:${Versions.ktor}"
|
||||
}
|
||||
|
||||
object SqlDelight {
|
||||
const val runtime = "com.squareup.sqldelight:runtime:${Versions.sqlDelight}"
|
||||
const val coroutineExtensions = "com.squareup.sqldelight:coroutines-extensions:${Versions.sqlDelight}"
|
||||
const val androidDriver = "com.squareup.sqldelight:android-driver:${Versions.sqlDelight}"
|
||||
const val nativeDriver = "com.squareup.sqldelight:native-driver:${Versions.sqlDelight}"
|
||||
const val nativeDriverMacos = "com.squareup.sqldelight:native-driver-macosx64:${Versions.sqlDelight}"
|
||||
const val sqliteDriver = "com.squareup.sqldelight:sqlite-driver:${Versions.sqlDelight}"
|
||||
}
|
||||
|
||||
object React {
|
||||
const val react = "org.jetbrains:kotlin-react:${Versions.kotlinReact}"
|
||||
const val dom = "org.jetbrains:kotlin-react-dom:${Versions.kotlinReactDom}"
|
||||
|
|
|
@ -6,7 +6,7 @@ plugins {
|
|||
id("kotlinx-serialization")
|
||||
id("com.android.library")
|
||||
id("org.jetbrains.kotlin.native.cocoapods")
|
||||
id("com.squareup.sqldelight")
|
||||
id("com.apollographql.apollo3")
|
||||
id("com.rickclephas.kmp.nativecoroutines")
|
||||
id("com.chromaticnoise.multiplatform-swiftpackage") version "2.0.3"
|
||||
}
|
||||
|
@ -62,22 +62,14 @@ kotlin {
|
|||
sourceSets {
|
||||
sourceSets["commonMain"].dependencies {
|
||||
|
||||
with(Deps.Ktor) {
|
||||
implementation(clientCore)
|
||||
implementation(clientJson)
|
||||
implementation(clientLogging)
|
||||
implementation(contentNegotiation)
|
||||
implementation(json)
|
||||
}
|
||||
|
||||
with(Deps.Kotlinx) {
|
||||
implementation(coroutinesCore)
|
||||
implementation(serializationCore)
|
||||
}
|
||||
|
||||
with(Deps.SqlDelight) {
|
||||
implementation(runtime)
|
||||
implementation(coroutineExtensions)
|
||||
with(Deps.Apollo) {
|
||||
implementation(apolloRuntime)
|
||||
implementation(apolloNormalizedCacheInMemory)
|
||||
}
|
||||
|
||||
with(Deps.Koin) {
|
||||
|
@ -97,38 +89,31 @@ kotlin {
|
|||
}
|
||||
|
||||
sourceSets["androidMain"].dependencies {
|
||||
implementation(Deps.Ktor.clientAndroid)
|
||||
implementation(Deps.SqlDelight.androidDriver)
|
||||
implementation(Deps.Apollo.apolloNormalizedCacheSqlite)
|
||||
}
|
||||
sourceSets["androidTest"].dependencies {
|
||||
implementation(Deps.Test.junit)
|
||||
}
|
||||
|
||||
sourceSets["jvmMain"].dependencies {
|
||||
implementation(Deps.Ktor.clientJava)
|
||||
implementation(Deps.SqlDelight.sqliteDriver)
|
||||
implementation(Deps.Log.slf4j)
|
||||
implementation(Deps.Apollo.apolloNormalizedCacheSqlite)
|
||||
}
|
||||
|
||||
sourceSets["iOSMain"].dependencies {
|
||||
implementation(Deps.Ktor.clientIos)
|
||||
implementation(Deps.SqlDelight.nativeDriver)
|
||||
implementation(Deps.Apollo.apolloNormalizedCacheSqlite)
|
||||
}
|
||||
sourceSets["iOSTest"].dependencies {
|
||||
}
|
||||
|
||||
sourceSets["watchMain"].dependencies {
|
||||
implementation(Deps.Ktor.clientIos)
|
||||
implementation(Deps.SqlDelight.nativeDriver)
|
||||
}
|
||||
|
||||
sourceSets["macOSMain"].dependencies {
|
||||
implementation(Deps.Ktor.clientIos)
|
||||
implementation(Deps.SqlDelight.nativeDriverMacos)
|
||||
implementation(Deps.Apollo.apolloNormalizedCacheSqlite)
|
||||
}
|
||||
|
||||
sourceSets["jsMain"].dependencies {
|
||||
implementation(Deps.Ktor.clientJs)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -139,17 +124,19 @@ tasks.withType<KotlinCompile> {
|
|||
}
|
||||
}
|
||||
|
||||
sqldelight {
|
||||
database("PeopleInSpaceDatabase") {
|
||||
packageName = "com.surrus.peopleinspace.db"
|
||||
sourceFolders = listOf("sqldelight")
|
||||
}
|
||||
apollo {
|
||||
packageName.set("com.surrus.common")
|
||||
codegenModels.set("operationBased")
|
||||
generateSchema.set(true)
|
||||
generateTestBuilders.set(true)
|
||||
}
|
||||
|
||||
|
||||
multiplatformSwiftPackage {
|
||||
packageName("PeopleInSpace")
|
||||
swiftToolsVersion("5.3")
|
||||
targetPlatforms {
|
||||
iOS { v("13") }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,19 +1,11 @@
|
|||
package com.surrus.common.repository
|
||||
|
||||
import com.squareup.sqldelight.android.AndroidSqliteDriver
|
||||
import com.surrus.common.di.PeopleInSpaceDatabaseWrapper
|
||||
import com.surrus.peopleinspace.db.PeopleInSpaceDatabase
|
||||
import io.ktor.client.engine.android.*
|
||||
import com.apollographql.apollo3.cache.normalized.api.NormalizedCacheFactory
|
||||
import com.apollographql.apollo3.cache.normalized.sql.SqlNormalizedCacheFactory
|
||||
import org.koin.dsl.module
|
||||
|
||||
|
||||
|
||||
actual fun platformModule() = module {
|
||||
single {
|
||||
val driver =
|
||||
AndroidSqliteDriver(PeopleInSpaceDatabase.Schema, get(), "peopleinspace.db")
|
||||
|
||||
PeopleInSpaceDatabaseWrapper(PeopleInSpaceDatabase(driver))
|
||||
}
|
||||
single { Android.create() }
|
||||
single<NormalizedCacheFactory> { SqlNormalizedCacheFactory(get(), "swapi.db") }
|
||||
}
|
||||
|
|
15
common/src/commonMain/graphql/Queries.graphql
Normal file
15
common/src/commonMain/graphql/Queries.graphql
Normal file
|
@ -0,0 +1,15 @@
|
|||
query GetPeople {
|
||||
people {
|
||||
name
|
||||
craft
|
||||
personBio
|
||||
personImageUrl
|
||||
}
|
||||
}
|
||||
|
||||
subscription IssPosition {
|
||||
issPosition {
|
||||
latitude
|
||||
longitude
|
||||
}
|
||||
}
|
850
common/src/commonMain/graphql/schema.json
Normal file
850
common/src/commonMain/graphql/schema.json
Normal file
|
@ -0,0 +1,850 @@
|
|||
{
|
||||
"__schema": {
|
||||
"queryType": {
|
||||
"name": "Query"
|
||||
},
|
||||
"subscriptionType": {
|
||||
"name": "Subscription"
|
||||
},
|
||||
"types": [
|
||||
{
|
||||
"kind": "SCALAR",
|
||||
"name": "Boolean",
|
||||
"description": "Built-in Boolean"
|
||||
},
|
||||
{
|
||||
"kind": "SCALAR",
|
||||
"name": "Float",
|
||||
"description": "Built-in Float"
|
||||
},
|
||||
{
|
||||
"kind": "OBJECT",
|
||||
"name": "Person",
|
||||
"fields": [
|
||||
{
|
||||
"name": "craft",
|
||||
"isDeprecated": false,
|
||||
"type": {
|
||||
"kind": "NON_NULL",
|
||||
"ofType": {
|
||||
"kind": "SCALAR",
|
||||
"name": "String"
|
||||
}
|
||||
},
|
||||
"args": []
|
||||
},
|
||||
{
|
||||
"name": "name",
|
||||
"isDeprecated": false,
|
||||
"type": {
|
||||
"kind": "NON_NULL",
|
||||
"ofType": {
|
||||
"kind": "SCALAR",
|
||||
"name": "String"
|
||||
}
|
||||
},
|
||||
"args": []
|
||||
},
|
||||
{
|
||||
"name": "personBio",
|
||||
"isDeprecated": false,
|
||||
"type": {
|
||||
"kind": "SCALAR",
|
||||
"name": "String"
|
||||
},
|
||||
"args": []
|
||||
},
|
||||
{
|
||||
"name": "personImageUrl",
|
||||
"isDeprecated": false,
|
||||
"type": {
|
||||
"kind": "SCALAR",
|
||||
"name": "String"
|
||||
},
|
||||
"args": []
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"kind": "OBJECT",
|
||||
"name": "Position",
|
||||
"fields": [
|
||||
{
|
||||
"name": "latitude",
|
||||
"isDeprecated": false,
|
||||
"type": {
|
||||
"kind": "NON_NULL",
|
||||
"ofType": {
|
||||
"kind": "SCALAR",
|
||||
"name": "Float"
|
||||
}
|
||||
},
|
||||
"args": []
|
||||
},
|
||||
{
|
||||
"name": "longitude",
|
||||
"isDeprecated": false,
|
||||
"type": {
|
||||
"kind": "NON_NULL",
|
||||
"ofType": {
|
||||
"kind": "SCALAR",
|
||||
"name": "Float"
|
||||
}
|
||||
},
|
||||
"args": []
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"kind": "OBJECT",
|
||||
"name": "Query",
|
||||
"fields": [
|
||||
{
|
||||
"name": "people",
|
||||
"isDeprecated": false,
|
||||
"type": {
|
||||
"kind": "NON_NULL",
|
||||
"ofType": {
|
||||
"kind": "LIST",
|
||||
"ofType": {
|
||||
"kind": "NON_NULL",
|
||||
"ofType": {
|
||||
"kind": "OBJECT",
|
||||
"name": "Person"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"args": []
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"kind": "SCALAR",
|
||||
"name": "String",
|
||||
"description": "Built-in String"
|
||||
},
|
||||
{
|
||||
"kind": "OBJECT",
|
||||
"name": "Subscription",
|
||||
"fields": [
|
||||
{
|
||||
"name": "issPosition",
|
||||
"isDeprecated": false,
|
||||
"type": {
|
||||
"kind": "NON_NULL",
|
||||
"ofType": {
|
||||
"kind": "OBJECT",
|
||||
"name": "Position"
|
||||
}
|
||||
},
|
||||
"args": []
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"kind": "OBJECT",
|
||||
"name": "__Directive",
|
||||
"fields": [
|
||||
{
|
||||
"name": "name",
|
||||
"description": "The __Directive type represents a Directive that a server supports.",
|
||||
"isDeprecated": false,
|
||||
"type": {
|
||||
"kind": "NON_NULL",
|
||||
"ofType": {
|
||||
"kind": "SCALAR",
|
||||
"name": "String"
|
||||
}
|
||||
},
|
||||
"args": []
|
||||
},
|
||||
{
|
||||
"name": "description",
|
||||
"isDeprecated": false,
|
||||
"type": {
|
||||
"kind": "SCALAR",
|
||||
"name": "String"
|
||||
},
|
||||
"args": []
|
||||
},
|
||||
{
|
||||
"name": "isRepeatable",
|
||||
"isDeprecated": false,
|
||||
"type": {
|
||||
"kind": "NON_NULL",
|
||||
"ofType": {
|
||||
"kind": "SCALAR",
|
||||
"name": "Boolean"
|
||||
}
|
||||
},
|
||||
"args": []
|
||||
},
|
||||
{
|
||||
"name": "locations",
|
||||
"isDeprecated": false,
|
||||
"type": {
|
||||
"kind": "NON_NULL",
|
||||
"ofType": {
|
||||
"kind": "LIST",
|
||||
"ofType": {
|
||||
"kind": "NON_NULL",
|
||||
"ofType": {
|
||||
"kind": "ENUM",
|
||||
"name": "__DirectiveLocation"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"args": []
|
||||
},
|
||||
{
|
||||
"name": "args",
|
||||
"isDeprecated": false,
|
||||
"type": {
|
||||
"kind": "NON_NULL",
|
||||
"ofType": {
|
||||
"kind": "LIST",
|
||||
"ofType": {
|
||||
"kind": "NON_NULL",
|
||||
"ofType": {
|
||||
"kind": "OBJECT",
|
||||
"name": "__InputValue"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"args": []
|
||||
},
|
||||
{
|
||||
"name": "onOperation",
|
||||
"isDeprecated": true,
|
||||
"deprecationReason": "Use `locations`.",
|
||||
"type": {
|
||||
"kind": "SCALAR",
|
||||
"name": "Boolean"
|
||||
},
|
||||
"args": []
|
||||
},
|
||||
{
|
||||
"name": "onFragment",
|
||||
"isDeprecated": true,
|
||||
"deprecationReason": "Use `locations`.",
|
||||
"type": {
|
||||
"kind": "SCALAR",
|
||||
"name": "Boolean"
|
||||
},
|
||||
"args": []
|
||||
},
|
||||
{
|
||||
"name": "onField",
|
||||
"isDeprecated": true,
|
||||
"deprecationReason": "Use `locations`.",
|
||||
"type": {
|
||||
"kind": "SCALAR",
|
||||
"name": "Boolean"
|
||||
},
|
||||
"args": []
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"kind": "ENUM",
|
||||
"name": "__DirectiveLocation",
|
||||
"description": "An enum describing valid locations where a directive can be placed",
|
||||
"enumValues": [
|
||||
{
|
||||
"name": "QUERY",
|
||||
"description": "Indicates the directive is valid on queries.",
|
||||
"isDeprecated": false
|
||||
},
|
||||
{
|
||||
"name": "MUTATION",
|
||||
"description": "Indicates the directive is valid on mutations.",
|
||||
"isDeprecated": false
|
||||
},
|
||||
{
|
||||
"name": "SUBSCRIPTION",
|
||||
"description": "Indicates the directive is valid on subscriptions.",
|
||||
"isDeprecated": false
|
||||
},
|
||||
{
|
||||
"name": "FIELD",
|
||||
"description": "Indicates the directive is valid on fields.",
|
||||
"isDeprecated": false
|
||||
},
|
||||
{
|
||||
"name": "FRAGMENT_DEFINITION",
|
||||
"description": "Indicates the directive is valid on fragment definitions.",
|
||||
"isDeprecated": false
|
||||
},
|
||||
{
|
||||
"name": "FRAGMENT_SPREAD",
|
||||
"description": "Indicates the directive is valid on fragment spreads.",
|
||||
"isDeprecated": false
|
||||
},
|
||||
{
|
||||
"name": "INLINE_FRAGMENT",
|
||||
"description": "Indicates the directive is valid on inline fragments.",
|
||||
"isDeprecated": false
|
||||
},
|
||||
{
|
||||
"name": "VARIABLE_DEFINITION",
|
||||
"description": "Indicates the directive is valid on variable definitions.",
|
||||
"isDeprecated": false
|
||||
},
|
||||
{
|
||||
"name": "SCHEMA",
|
||||
"description": "Indicates the directive is valid on a schema SDL definition.",
|
||||
"isDeprecated": false
|
||||
},
|
||||
{
|
||||
"name": "SCALAR",
|
||||
"description": "Indicates the directive is valid on a scalar SDL definition.",
|
||||
"isDeprecated": false
|
||||
},
|
||||
{
|
||||
"name": "OBJECT",
|
||||
"description": "Indicates the directive is valid on an object SDL definition.",
|
||||
"isDeprecated": false
|
||||
},
|
||||
{
|
||||
"name": "FIELD_DEFINITION",
|
||||
"description": "Indicates the directive is valid on a field SDL definition.",
|
||||
"isDeprecated": false
|
||||
},
|
||||
{
|
||||
"name": "ARGUMENT_DEFINITION",
|
||||
"description": "Indicates the directive is valid on a field argument SDL definition.",
|
||||
"isDeprecated": false
|
||||
},
|
||||
{
|
||||
"name": "INTERFACE",
|
||||
"description": "Indicates the directive is valid on an interface SDL definition.",
|
||||
"isDeprecated": false
|
||||
},
|
||||
{
|
||||
"name": "UNION",
|
||||
"description": "Indicates the directive is valid on an union SDL definition.",
|
||||
"isDeprecated": false
|
||||
},
|
||||
{
|
||||
"name": "ENUM",
|
||||
"description": "Indicates the directive is valid on an enum SDL definition.",
|
||||
"isDeprecated": false
|
||||
},
|
||||
{
|
||||
"name": "ENUM_VALUE",
|
||||
"description": "Indicates the directive is valid on an enum value SDL definition.",
|
||||
"isDeprecated": false
|
||||
},
|
||||
{
|
||||
"name": "INPUT_OBJECT",
|
||||
"description": "Indicates the directive is valid on an input object SDL definition.",
|
||||
"isDeprecated": false
|
||||
},
|
||||
{
|
||||
"name": "INPUT_FIELD_DEFINITION",
|
||||
"description": "Indicates the directive is valid on an input object field SDL definition.",
|
||||
"isDeprecated": false
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"kind": "OBJECT",
|
||||
"name": "__EnumValue",
|
||||
"fields": [
|
||||
{
|
||||
"name": "name",
|
||||
"isDeprecated": false,
|
||||
"type": {
|
||||
"kind": "NON_NULL",
|
||||
"ofType": {
|
||||
"kind": "SCALAR",
|
||||
"name": "String"
|
||||
}
|
||||
},
|
||||
"args": []
|
||||
},
|
||||
{
|
||||
"name": "description",
|
||||
"isDeprecated": false,
|
||||
"type": {
|
||||
"kind": "SCALAR",
|
||||
"name": "String"
|
||||
},
|
||||
"args": []
|
||||
},
|
||||
{
|
||||
"name": "isDeprecated",
|
||||
"isDeprecated": false,
|
||||
"type": {
|
||||
"kind": "NON_NULL",
|
||||
"ofType": {
|
||||
"kind": "SCALAR",
|
||||
"name": "Boolean"
|
||||
}
|
||||
},
|
||||
"args": []
|
||||
},
|
||||
{
|
||||
"name": "deprecationReason",
|
||||
"isDeprecated": false,
|
||||
"type": {
|
||||
"kind": "SCALAR",
|
||||
"name": "String"
|
||||
},
|
||||
"args": []
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"kind": "OBJECT",
|
||||
"name": "__Field",
|
||||
"fields": [
|
||||
{
|
||||
"name": "name",
|
||||
"isDeprecated": false,
|
||||
"type": {
|
||||
"kind": "NON_NULL",
|
||||
"ofType": {
|
||||
"kind": "SCALAR",
|
||||
"name": "String"
|
||||
}
|
||||
},
|
||||
"args": []
|
||||
},
|
||||
{
|
||||
"name": "description",
|
||||
"isDeprecated": false,
|
||||
"type": {
|
||||
"kind": "SCALAR",
|
||||
"name": "String"
|
||||
},
|
||||
"args": []
|
||||
},
|
||||
{
|
||||
"name": "args",
|
||||
"isDeprecated": false,
|
||||
"type": {
|
||||
"kind": "NON_NULL",
|
||||
"ofType": {
|
||||
"kind": "LIST",
|
||||
"ofType": {
|
||||
"kind": "NON_NULL",
|
||||
"ofType": {
|
||||
"kind": "OBJECT",
|
||||
"name": "__InputValue"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"args": [
|
||||
{
|
||||
"name": "includeDeprecated",
|
||||
"isDeprecated": false,
|
||||
"type": {
|
||||
"kind": "SCALAR",
|
||||
"name": "Boolean"
|
||||
},
|
||||
"defaultValue": "false"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "type",
|
||||
"isDeprecated": false,
|
||||
"type": {
|
||||
"kind": "NON_NULL",
|
||||
"ofType": {
|
||||
"kind": "OBJECT",
|
||||
"name": "__Type"
|
||||
}
|
||||
},
|
||||
"args": []
|
||||
},
|
||||
{
|
||||
"name": "isDeprecated",
|
||||
"isDeprecated": false,
|
||||
"type": {
|
||||
"kind": "NON_NULL",
|
||||
"ofType": {
|
||||
"kind": "SCALAR",
|
||||
"name": "Boolean"
|
||||
}
|
||||
},
|
||||
"args": []
|
||||
},
|
||||
{
|
||||
"name": "deprecationReason",
|
||||
"isDeprecated": false,
|
||||
"type": {
|
||||
"kind": "SCALAR",
|
||||
"name": "String"
|
||||
},
|
||||
"args": []
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"kind": "OBJECT",
|
||||
"name": "__InputValue",
|
||||
"fields": [
|
||||
{
|
||||
"name": "name",
|
||||
"isDeprecated": false,
|
||||
"type": {
|
||||
"kind": "NON_NULL",
|
||||
"ofType": {
|
||||
"kind": "SCALAR",
|
||||
"name": "String"
|
||||
}
|
||||
},
|
||||
"args": []
|
||||
},
|
||||
{
|
||||
"name": "description",
|
||||
"isDeprecated": false,
|
||||
"type": {
|
||||
"kind": "SCALAR",
|
||||
"name": "String"
|
||||
},
|
||||
"args": []
|
||||
},
|
||||
{
|
||||
"name": "type",
|
||||
"isDeprecated": false,
|
||||
"type": {
|
||||
"kind": "NON_NULL",
|
||||
"ofType": {
|
||||
"kind": "OBJECT",
|
||||
"name": "__Type"
|
||||
}
|
||||
},
|
||||
"args": []
|
||||
},
|
||||
{
|
||||
"name": "defaultValue",
|
||||
"isDeprecated": false,
|
||||
"type": {
|
||||
"kind": "SCALAR",
|
||||
"name": "String"
|
||||
},
|
||||
"args": []
|
||||
},
|
||||
{
|
||||
"name": "isDeprecated",
|
||||
"isDeprecated": false,
|
||||
"type": {
|
||||
"kind": "SCALAR",
|
||||
"name": "Boolean"
|
||||
},
|
||||
"args": []
|
||||
},
|
||||
{
|
||||
"name": "deprecationReason",
|
||||
"isDeprecated": false,
|
||||
"type": {
|
||||
"kind": "SCALAR",
|
||||
"name": "String"
|
||||
},
|
||||
"args": []
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"kind": "OBJECT",
|
||||
"name": "__Schema",
|
||||
"description": "A GraphQL Introspection defines the capabilities of a GraphQL server. It exposes all available types and directives on the server, the entry points for query, mutation, and subscription operations.",
|
||||
"fields": [
|
||||
{
|
||||
"name": "description",
|
||||
"isDeprecated": false,
|
||||
"type": {
|
||||
"kind": "SCALAR",
|
||||
"name": "String"
|
||||
},
|
||||
"args": []
|
||||
},
|
||||
{
|
||||
"name": "types",
|
||||
"description": "A list of all types supported by this server.",
|
||||
"isDeprecated": false,
|
||||
"type": {
|
||||
"kind": "NON_NULL",
|
||||
"ofType": {
|
||||
"kind": "LIST",
|
||||
"ofType": {
|
||||
"kind": "NON_NULL",
|
||||
"ofType": {
|
||||
"kind": "OBJECT",
|
||||
"name": "__Type"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"args": []
|
||||
},
|
||||
{
|
||||
"name": "queryType",
|
||||
"description": "The type that query operations will be rooted at.",
|
||||
"isDeprecated": false,
|
||||
"type": {
|
||||
"kind": "NON_NULL",
|
||||
"ofType": {
|
||||
"kind": "OBJECT",
|
||||
"name": "__Type"
|
||||
}
|
||||
},
|
||||
"args": []
|
||||
},
|
||||
{
|
||||
"name": "mutationType",
|
||||
"description": "If this server supports mutation, the type that mutation operations will be rooted at.",
|
||||
"isDeprecated": false,
|
||||
"type": {
|
||||
"kind": "OBJECT",
|
||||
"name": "__Type"
|
||||
},
|
||||
"args": []
|
||||
},
|
||||
{
|
||||
"name": "directives",
|
||||
"description": "'A list of all directives supported by this server.",
|
||||
"isDeprecated": false,
|
||||
"type": {
|
||||
"kind": "NON_NULL",
|
||||
"ofType": {
|
||||
"kind": "LIST",
|
||||
"ofType": {
|
||||
"kind": "NON_NULL",
|
||||
"ofType": {
|
||||
"kind": "OBJECT",
|
||||
"name": "__Directive"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"args": []
|
||||
},
|
||||
{
|
||||
"name": "subscriptionType",
|
||||
"description": "'If this server support subscription, the type that subscription operations will be rooted at.",
|
||||
"isDeprecated": false,
|
||||
"type": {
|
||||
"kind": "OBJECT",
|
||||
"name": "__Type"
|
||||
},
|
||||
"args": []
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"kind": "OBJECT",
|
||||
"name": "__Type",
|
||||
"fields": [
|
||||
{
|
||||
"name": "kind",
|
||||
"isDeprecated": false,
|
||||
"type": {
|
||||
"kind": "NON_NULL",
|
||||
"ofType": {
|
||||
"kind": "ENUM",
|
||||
"name": "__TypeKind"
|
||||
}
|
||||
},
|
||||
"args": []
|
||||
},
|
||||
{
|
||||
"name": "name",
|
||||
"isDeprecated": false,
|
||||
"type": {
|
||||
"kind": "SCALAR",
|
||||
"name": "String"
|
||||
},
|
||||
"args": []
|
||||
},
|
||||
{
|
||||
"name": "description",
|
||||
"isDeprecated": false,
|
||||
"type": {
|
||||
"kind": "SCALAR",
|
||||
"name": "String"
|
||||
},
|
||||
"args": []
|
||||
},
|
||||
{
|
||||
"name": "fields",
|
||||
"isDeprecated": false,
|
||||
"type": {
|
||||
"kind": "LIST",
|
||||
"ofType": {
|
||||
"kind": "NON_NULL",
|
||||
"ofType": {
|
||||
"kind": "OBJECT",
|
||||
"name": "__Field"
|
||||
}
|
||||
}
|
||||
},
|
||||
"args": [
|
||||
{
|
||||
"name": "includeDeprecated",
|
||||
"isDeprecated": false,
|
||||
"type": {
|
||||
"kind": "SCALAR",
|
||||
"name": "Boolean"
|
||||
},
|
||||
"defaultValue": "false"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "interfaces",
|
||||
"isDeprecated": false,
|
||||
"type": {
|
||||
"kind": "LIST",
|
||||
"ofType": {
|
||||
"kind": "NON_NULL",
|
||||
"ofType": {
|
||||
"kind": "OBJECT",
|
||||
"name": "__Type"
|
||||
}
|
||||
}
|
||||
},
|
||||
"args": []
|
||||
},
|
||||
{
|
||||
"name": "possibleTypes",
|
||||
"isDeprecated": false,
|
||||
"type": {
|
||||
"kind": "LIST",
|
||||
"ofType": {
|
||||
"kind": "NON_NULL",
|
||||
"ofType": {
|
||||
"kind": "OBJECT",
|
||||
"name": "__Type"
|
||||
}
|
||||
}
|
||||
},
|
||||
"args": []
|
||||
},
|
||||
{
|
||||
"name": "enumValues",
|
||||
"isDeprecated": false,
|
||||
"type": {
|
||||
"kind": "LIST",
|
||||
"ofType": {
|
||||
"kind": "NON_NULL",
|
||||
"ofType": {
|
||||
"kind": "OBJECT",
|
||||
"name": "__EnumValue"
|
||||
}
|
||||
}
|
||||
},
|
||||
"args": [
|
||||
{
|
||||
"name": "includeDeprecated",
|
||||
"isDeprecated": false,
|
||||
"type": {
|
||||
"kind": "SCALAR",
|
||||
"name": "Boolean"
|
||||
},
|
||||
"defaultValue": "false"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "inputFields",
|
||||
"isDeprecated": false,
|
||||
"type": {
|
||||
"kind": "LIST",
|
||||
"ofType": {
|
||||
"kind": "NON_NULL",
|
||||
"ofType": {
|
||||
"kind": "OBJECT",
|
||||
"name": "__InputValue"
|
||||
}
|
||||
}
|
||||
},
|
||||
"args": [
|
||||
{
|
||||
"name": "includeDeprecated",
|
||||
"isDeprecated": false,
|
||||
"type": {
|
||||
"kind": "SCALAR",
|
||||
"name": "Boolean"
|
||||
},
|
||||
"defaultValue": "false"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "ofType",
|
||||
"isDeprecated": false,
|
||||
"type": {
|
||||
"kind": "OBJECT",
|
||||
"name": "__Type"
|
||||
},
|
||||
"args": []
|
||||
},
|
||||
{
|
||||
"name": "specifiedByUrl",
|
||||
"isDeprecated": false,
|
||||
"type": {
|
||||
"kind": "SCALAR",
|
||||
"name": "String"
|
||||
},
|
||||
"args": []
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"kind": "ENUM",
|
||||
"name": "__TypeKind",
|
||||
"description": "An enum describing what kind of type a given __Type is",
|
||||
"enumValues": [
|
||||
{
|
||||
"name": "SCALAR",
|
||||
"description": "Indicates this type is a scalar. 'specifiedByUrl' is a valid field",
|
||||
"isDeprecated": false
|
||||
},
|
||||
{
|
||||
"name": "OBJECT",
|
||||
"description": "Indicates this type is an object. `fields` and `interfaces` are valid fields.",
|
||||
"isDeprecated": false
|
||||
},
|
||||
{
|
||||
"name": "INTERFACE",
|
||||
"description": "Indicates this type is an interface. `fields` and `possibleTypes` are valid fields.",
|
||||
"isDeprecated": false
|
||||
},
|
||||
{
|
||||
"name": "UNION",
|
||||
"description": "Indicates this type is a union. `possibleTypes` is a valid field.",
|
||||
"isDeprecated": false
|
||||
},
|
||||
{
|
||||
"name": "ENUM",
|
||||
"description": "Indicates this type is an enum. `enumValues` is a valid field.",
|
||||
"isDeprecated": false
|
||||
},
|
||||
{
|
||||
"name": "INPUT_OBJECT",
|
||||
"description": "Indicates this type is an input object. `inputFields` is a valid field.",
|
||||
"isDeprecated": false
|
||||
},
|
||||
{
|
||||
"name": "LIST",
|
||||
"description": "Indicates this type is a list. `ofType` is a valid field.",
|
||||
"isDeprecated": false
|
||||
},
|
||||
{
|
||||
"name": "NON_NULL",
|
||||
"description": "Indicates this type is a non-null. `ofType` is a valid field.",
|
||||
"isDeprecated": false
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
|
@ -1,18 +1,15 @@
|
|||
package com.surrus.common.di
|
||||
|
||||
import com.surrus.common.remote.PeopleInSpaceApi
|
||||
import com.apollographql.apollo3.ApolloClient
|
||||
import com.apollographql.apollo3.cache.normalized.api.MemoryCacheFactory
|
||||
import com.apollographql.apollo3.cache.normalized.api.NormalizedCacheFactory
|
||||
import com.apollographql.apollo3.cache.normalized.normalizedCache
|
||||
import com.surrus.common.repository.PeopleInSpaceRepository
|
||||
import com.surrus.common.repository.PeopleInSpaceRepositoryInterface
|
||||
import com.surrus.common.repository.platformModule
|
||||
import io.ktor.client.*
|
||||
import io.ktor.client.engine.*
|
||||
import io.ktor.client.plugins.*
|
||||
import io.ktor.client.plugins.logging.*
|
||||
import io.ktor.serialization.kotlinx.json.*
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.SupervisorJob
|
||||
import kotlinx.serialization.json.Json
|
||||
import org.koin.core.context.startKoin
|
||||
import org.koin.dsl.KoinAppDeclaration
|
||||
import org.koin.dsl.module
|
||||
|
@ -27,27 +24,23 @@ fun initKoin(enableNetworkLogs: Boolean = false, appDeclaration: KoinAppDeclarat
|
|||
fun initKoin() = initKoin(enableNetworkLogs = false) {}
|
||||
|
||||
fun commonModule(enableNetworkLogs: Boolean) = module {
|
||||
single { createJson() }
|
||||
single { createHttpClient(get(), get(), enableNetworkLogs = enableNetworkLogs) }
|
||||
|
||||
single { CoroutineScope(Dispatchers.Default + SupervisorJob() ) }
|
||||
|
||||
single<PeopleInSpaceRepositoryInterface> { PeopleInSpaceRepository() }
|
||||
|
||||
single { PeopleInSpaceApi(get()) }
|
||||
|
||||
single { createApolloClient(get()) }
|
||||
}
|
||||
|
||||
fun createJson() = Json { isLenient = true; ignoreUnknownKeys = true }
|
||||
|
||||
|
||||
fun createHttpClient(httpClientEngine: HttpClientEngine, json: Json, enableNetworkLogs: Boolean) = HttpClient(httpClientEngine) {
|
||||
install(ContentNegotiation) {
|
||||
json(json)
|
||||
}
|
||||
if (enableNetworkLogs) {
|
||||
install(Logging) {
|
||||
logger = Logger.DEFAULT
|
||||
level = LogLevel.INFO
|
||||
}
|
||||
}
|
||||
fun createApolloClient(sqlNormalizedCacheFactory: NormalizedCacheFactory): ApolloClient {
|
||||
val memoryFirstThenSqlCacheFactory = MemoryCacheFactory(10 * 1024 * 1024)
|
||||
.chain(sqlNormalizedCacheFactory)
|
||||
|
||||
return ApolloClient.Builder()
|
||||
.serverUrl("https://peopleinspace-graphql-guhrsfr7ka-uc.a.run.app/graphql")
|
||||
.webSocketServerUrl("wss://peopleinspace-graphql-guhrsfr7ka-uc.a.run.app/subscriptions")
|
||||
.normalizedCache(memoryFirstThenSqlCacheFactory, writeToCacheAsynchronously = true)
|
||||
.build()
|
||||
}
|
||||
|
|
|
@ -1,5 +0,0 @@
|
|||
package com.surrus.common.di
|
||||
|
||||
import com.surrus.peopleinspace.db.PeopleInSpaceDatabase
|
||||
|
||||
class PeopleInSpaceDatabaseWrapper(val instance: PeopleInSpaceDatabase?)
|
|
@ -0,0 +1,9 @@
|
|||
package com.surrus.common.model
|
||||
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
data class Assignment(val craft: String, val name: String, var personImageUrl: String? = "", var personBio: String? = "")
|
||||
|
||||
@Serializable
|
||||
data class IssPosition(val latitude: Double, val longitude: Double)
|
|
@ -1,27 +0,0 @@
|
|||
package com.surrus.common.remote
|
||||
|
||||
import io.ktor.client.*
|
||||
import io.ktor.client.call.*
|
||||
import io.ktor.client.request.*
|
||||
import kotlinx.serialization.Serializable
|
||||
import org.koin.core.component.KoinComponent
|
||||
|
||||
@Serializable
|
||||
data class AstroResult(val message: String, val number: Int, val people: List<Assignment>)
|
||||
|
||||
@Serializable
|
||||
data class Assignment(val craft: String, val name: String, var personImageUrl: String? = "", var personBio: String? = "")
|
||||
|
||||
@Serializable
|
||||
data class IssPosition(val latitude: Double, val longitude: Double)
|
||||
|
||||
@Serializable
|
||||
data class IssResponse(val message: String, val iss_position: IssPosition, val timestamp: Long)
|
||||
|
||||
class PeopleInSpaceApi(
|
||||
private val client: HttpClient,
|
||||
var baseUrl: String = "https://people-in-space-proxy.ew.r.appspot.com",
|
||||
) : KoinComponent {
|
||||
suspend fun fetchPeople() = client.get("$baseUrl/astros.json").body<AstroResult>()
|
||||
suspend fun fetchISSPosition() = client.get("$baseUrl/iss-now.json").body<IssResponse>()
|
||||
}
|
|
@ -1,18 +1,16 @@
|
|||
package com.surrus.common.repository
|
||||
|
||||
import co.touchlab.kermit.Logger
|
||||
import com.rickclephas.kmp.nativecoroutines.NativeCoroutineScope
|
||||
import com.squareup.sqldelight.runtime.coroutines.asFlow
|
||||
import com.squareup.sqldelight.runtime.coroutines.mapToList
|
||||
import com.surrus.common.di.PeopleInSpaceDatabaseWrapper
|
||||
import com.surrus.common.remote.Assignment
|
||||
import com.surrus.common.remote.IssPosition
|
||||
import com.surrus.common.remote.PeopleInSpaceApi
|
||||
import kotlinx.coroutines.*
|
||||
import com.apollographql.apollo3.ApolloClient
|
||||
import com.apollographql.apollo3.cache.normalized.watch
|
||||
import com.surrus.common.GetPeopleQuery
|
||||
import com.surrus.common.IssPositionSubscription
|
||||
import com.surrus.common.model.Assignment
|
||||
import com.surrus.common.model.IssPosition
|
||||
import kotlinx.coroutines.flow.*
|
||||
import org.koin.core.component.KoinComponent
|
||||
import org.koin.core.component.inject
|
||||
|
||||
|
||||
interface PeopleInSpaceRepositoryInterface {
|
||||
fun fetchPeopleAsFlow(): Flow<List<Assignment>>
|
||||
fun pollISSPosition(): Flow<IssPosition>
|
||||
|
@ -20,72 +18,30 @@ interface PeopleInSpaceRepositoryInterface {
|
|||
suspend fun fetchAndStorePeople()
|
||||
}
|
||||
|
||||
fun GetPeopleQuery.Person.mapToAssignment() = Assignment(craft, name, personImageUrl, personBio)
|
||||
|
||||
class PeopleInSpaceRepository : KoinComponent, PeopleInSpaceRepositoryInterface {
|
||||
private val peopleInSpaceApi: PeopleInSpaceApi by inject()
|
||||
private val apolloClient: ApolloClient by inject()
|
||||
|
||||
@NativeCoroutineScope
|
||||
private val coroutineScope: CoroutineScope = MainScope()
|
||||
private val peopleInSpaceDatabase: PeopleInSpaceDatabaseWrapper by inject()
|
||||
private val peopleInSpaceQueries = peopleInSpaceDatabase.instance?.peopleInSpaceQueries
|
||||
|
||||
val logger = Logger.withTag("PeopleInSpaceRepository")
|
||||
|
||||
init {
|
||||
coroutineScope.launch {
|
||||
fetchAndStorePeople()
|
||||
override fun fetchPeopleAsFlow() =
|
||||
apolloClient.query(GetPeopleQuery()).watch().map {
|
||||
it.dataAssertNoErrors.people.map { it.mapToAssignment() }
|
||||
}
|
||||
}
|
||||
|
||||
override fun fetchPeopleAsFlow(): Flow<List<Assignment>> {
|
||||
// the main reason we need to do this check is that sqldelight isn't currently
|
||||
// setup for javascript client
|
||||
return peopleInSpaceQueries?.selectAll(
|
||||
mapper = { name, craft, personImageUrl, personBio ->
|
||||
Assignment(name = name, craft = craft, personImageUrl = personImageUrl, personBio = personBio)
|
||||
}
|
||||
)?.asFlow()?.mapToList() ?: flowOf(emptyList())
|
||||
override suspend fun fetchPeople(): List<Assignment> {
|
||||
val response = apolloClient.query(GetPeopleQuery()).execute()
|
||||
return response.dataAssertNoErrors.people.map {
|
||||
it.mapToAssignment()
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun fetchAndStorePeople() {
|
||||
logger.d { "fetchAndStorePeople" }
|
||||
try {
|
||||
val result = peopleInSpaceApi.fetchPeople()
|
||||
fetchPeople()
|
||||
}
|
||||
|
||||
// this is very basic implementation for now that removes all existing rows
|
||||
// in db and then inserts results from api request
|
||||
// using "transaction" accelerate the batch of queries, especially inserting
|
||||
peopleInSpaceQueries?.transaction {
|
||||
peopleInSpaceQueries.deleteAll()
|
||||
result.people.forEach {
|
||||
peopleInSpaceQueries.insertItem(
|
||||
it.name,
|
||||
it.craft,
|
||||
it.personImageUrl,
|
||||
it.personBio
|
||||
)
|
||||
}
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
// TODO report error up to UI
|
||||
logger.w(e) { "Exception during fetchAndStorePeople: $e" }
|
||||
override fun pollISSPosition() =
|
||||
apolloClient.subscription(IssPositionSubscription()).toFlow().map {
|
||||
val result = it.dataAssertNoErrors.issPosition
|
||||
IssPosition(result.latitude, result.longitude)
|
||||
}
|
||||
}
|
||||
|
||||
// Used by web and apple clients atm
|
||||
override suspend fun fetchPeople(): List<Assignment> = peopleInSpaceApi.fetchPeople().people
|
||||
|
||||
override fun pollISSPosition(): Flow<IssPosition> {
|
||||
return flow {
|
||||
while (true) {
|
||||
val position = peopleInSpaceApi.fetchISSPosition().iss_position
|
||||
emit(position)
|
||||
logger.d { position.toString() }
|
||||
delay(POLL_INTERVAL)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val POLL_INTERVAL = 10000L
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,2 +0,0 @@
|
|||
ALTER TABLE People ADD COLUMN personImageUrl TEXT;
|
||||
ALTER TABLE People ADD COLUMN personBio TEXT;
|
|
@ -1,16 +0,0 @@
|
|||
|
||||
CREATE TABLE People(
|
||||
name TEXT NOT NULL PRIMARY KEY,
|
||||
craft TEXT NOT NULL,
|
||||
personImageUrl TEXT,
|
||||
personBio TEXT
|
||||
);
|
||||
|
||||
insertItem:
|
||||
INSERT OR REPLACE INTO People(name, craft, personImageUrl, personBio)VALUES(?,?,?,?);
|
||||
|
||||
selectAll:
|
||||
SELECT * FROM People;
|
||||
|
||||
deleteAll:
|
||||
DELETE FROM People;
|
|
@ -1,6 +1,6 @@
|
|||
package com.surrus.peopleinspace
|
||||
|
||||
import com.surrus.common.di.PeopleInSpaceDatabaseWrapper
|
||||
import com.apollographql.apollo3.ApolloClient
|
||||
import com.surrus.common.di.commonModule
|
||||
import com.surrus.common.repository.PeopleInSpaceRepositoryInterface
|
||||
import com.surrus.common.repository.platformModule
|
||||
|
@ -28,7 +28,7 @@ class PeopleInSpaceTest: KoinTest {
|
|||
commonModule(true),
|
||||
platformModule(),
|
||||
module {
|
||||
single { PeopleInSpaceDatabaseWrapper(null) }
|
||||
single { createMockApolloClient("https://peopleinspace-graphql-guhrsfr7ka-uc.a.run.app/graphql") }
|
||||
}
|
||||
)
|
||||
}
|
||||
|
@ -40,4 +40,11 @@ class PeopleInSpaceTest: KoinTest {
|
|||
println(result)
|
||||
assertTrue(result.isNotEmpty())
|
||||
}
|
||||
|
||||
private fun createMockApolloClient(url: String): ApolloClient {
|
||||
println("createMockApolloClient, ur = $url")
|
||||
return ApolloClient.Builder()
|
||||
.serverUrl(url)
|
||||
.build()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,16 +1,11 @@
|
|||
package com.surrus.common.repository
|
||||
|
||||
import com.apollographql.apollo3.cache.normalized.api.NormalizedCacheFactory
|
||||
import com.apollographql.apollo3.cache.normalized.sql.SqlNormalizedCacheFactory
|
||||
import com.squareup.sqldelight.drivers.native.NativeSqliteDriver
|
||||
import com.surrus.common.di.PeopleInSpaceDatabaseWrapper
|
||||
import com.surrus.peopleinspace.db.PeopleInSpaceDatabase
|
||||
import io.ktor.client.engine.ios.*
|
||||
import org.koin.dsl.module
|
||||
|
||||
|
||||
actual fun platformModule() = module {
|
||||
single {
|
||||
val driver = NativeSqliteDriver(PeopleInSpaceDatabase.Schema, "peopleinspace.db")
|
||||
PeopleInSpaceDatabaseWrapper(PeopleInSpaceDatabase(driver))
|
||||
}
|
||||
single { Ios.create() }
|
||||
single<NormalizedCacheFactory> { SqlNormalizedCacheFactory("swapi.db") }
|
||||
}
|
||||
|
|
|
@ -1,12 +1,9 @@
|
|||
package com.surrus.common.repository
|
||||
|
||||
import com.surrus.common.di.PeopleInSpaceDatabaseWrapper
|
||||
import io.ktor.client.engine.js.*
|
||||
import com.apollographql.apollo3.cache.normalized.api.MemoryCacheFactory
|
||||
import com.apollographql.apollo3.cache.normalized.api.NormalizedCacheFactory
|
||||
import org.koin.dsl.module
|
||||
|
||||
actual fun platformModule() = module {
|
||||
single {
|
||||
PeopleInSpaceDatabaseWrapper(null)
|
||||
}
|
||||
single { Js.create() }
|
||||
single<NormalizedCacheFactory> { MemoryCacheFactory() }
|
||||
}
|
||||
|
|
|
@ -1,13 +0,0 @@
|
|||
package com.surrus
|
||||
|
||||
import com.surrus.common.di.initKoin
|
||||
import com.surrus.common.remote.PeopleInSpaceApi
|
||||
import kotlinx.coroutines.runBlocking
|
||||
|
||||
fun main() {
|
||||
runBlocking {
|
||||
val koin = initKoin(enableNetworkLogs = true).koin
|
||||
val api = koin.get<PeopleInSpaceApi>()
|
||||
println(api.fetchPeople())
|
||||
}
|
||||
}
|
|
@ -1,16 +1,9 @@
|
|||
package com.surrus.common.repository
|
||||
|
||||
import com.squareup.sqldelight.sqlite.driver.JdbcSqliteDriver
|
||||
import com.surrus.common.di.PeopleInSpaceDatabaseWrapper
|
||||
import com.surrus.peopleinspace.db.PeopleInSpaceDatabase
|
||||
import io.ktor.client.engine.java.*
|
||||
import com.apollographql.apollo3.cache.normalized.api.NormalizedCacheFactory
|
||||
import com.apollographql.apollo3.cache.normalized.sql.SqlNormalizedCacheFactory
|
||||
import org.koin.dsl.module
|
||||
|
||||
actual fun platformModule() = module {
|
||||
single {
|
||||
val driver = JdbcSqliteDriver(JdbcSqliteDriver.IN_MEMORY)
|
||||
.also { PeopleInSpaceDatabase.Schema.create(it) }
|
||||
PeopleInSpaceDatabaseWrapper(PeopleInSpaceDatabase(driver))
|
||||
}
|
||||
single { Java.create() }
|
||||
single<NormalizedCacheFactory> { SqlNormalizedCacheFactory("jdbc:sqlite:swapi.db") }
|
||||
}
|
||||
|
|
|
@ -1,15 +1,10 @@
|
|||
package com.surrus.common.repository
|
||||
|
||||
import com.apollographql.apollo3.cache.normalized.api.NormalizedCacheFactory
|
||||
import com.apollographql.apollo3.cache.normalized.sql.SqlNormalizedCacheFactory
|
||||
import com.squareup.sqldelight.drivers.native.NativeSqliteDriver
|
||||
import com.surrus.common.di.PeopleInSpaceDatabaseWrapper
|
||||
import com.surrus.peopleinspace.db.PeopleInSpaceDatabase
|
||||
import io.ktor.client.engine.ios.*
|
||||
import org.koin.dsl.module
|
||||
|
||||
actual fun platformModule() = module {
|
||||
single {
|
||||
val driver = NativeSqliteDriver(PeopleInSpaceDatabase.Schema, "peopleinspace.db")
|
||||
PeopleInSpaceDatabaseWrapper(PeopleInSpaceDatabase(driver))
|
||||
}
|
||||
single { Ios.create() }
|
||||
single<NormalizedCacheFactory> { SqlNormalizedCacheFactory("swapi.db") }
|
||||
}
|
||||
|
|
|
@ -19,8 +19,9 @@ import androidx.compose.ui.window.Window
|
|||
import androidx.compose.ui.window.application
|
||||
import androidx.compose.ui.window.rememberWindowState
|
||||
import com.surrus.common.di.initKoin
|
||||
import com.surrus.common.remote.Assignment
|
||||
import com.surrus.common.remote.PeopleInSpaceApi
|
||||
import com.surrus.common.repository.PeopleInSpaceRepositoryInterface
|
||||
import com.surrus.common.model.IssPosition
|
||||
import com.surrus.common.model.Assignment
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
import org.jetbrains.skia.Image.Companion.makeFromEncoded
|
||||
|
@ -39,10 +40,10 @@ fun main() = application {
|
|||
var peopleState by remember { mutableStateOf(emptyList<Assignment>()) }
|
||||
var selectedPerson by remember { mutableStateOf<Assignment?>(null) }
|
||||
|
||||
val peopleInSpaceApi = koin.get<PeopleInSpaceApi>()
|
||||
val repository = koin.get<PeopleInSpaceRepositoryInterface>()
|
||||
|
||||
LaunchedEffect(true) {
|
||||
peopleState = peopleInSpaceApi.fetchPeople().people
|
||||
peopleState = repository.fetchPeople()
|
||||
selectedPerson = peopleState.first()
|
||||
}
|
||||
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
import androidx.compose.runtime.*
|
||||
import com.surrus.common.di.initKoin
|
||||
import com.surrus.common.remote.Assignment
|
||||
import com.surrus.common.remote.IssPosition
|
||||
import com.surrus.common.repository.PeopleInSpaceRepository
|
||||
import com.surrus.common.model.Assignment
|
||||
import com.surrus.common.model.IssPosition
|
||||
import com.surrus.common.repository.PeopleInSpaceRepositoryInterface
|
||||
import kotlinx.coroutines.InternalCoroutinesApi
|
||||
import kotlinx.coroutines.flow.collect
|
||||
|
|
2
gradle/wrapper/gradle-wrapper.properties
vendored
2
gradle/wrapper/gradle-wrapper.properties
vendored
|
@ -2,4 +2,4 @@ distributionBase=GRADLE_USER_HOME
|
|||
distributionPath=wrapper/dists
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-7.0.2-bin.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-bin.zip
|
||||
|
|
1
graphql-server/.gitignore
vendored
1
graphql-server/.gitignore
vendored
|
@ -1 +0,0 @@
|
|||
build
|
|
@ -1,41 +0,0 @@
|
|||
plugins {
|
||||
id("kotlin-platform-jvm")
|
||||
id("org.jetbrains.kotlin.plugin.spring") version("1.6.10")
|
||||
id("org.jetbrains.kotlin.plugin.serialization")
|
||||
id("org.springframework.boot") version("2.5.6")
|
||||
id("com.google.cloud.tools.appengine") version("2.4.2")
|
||||
id("com.github.johnrengelman.shadow")
|
||||
}
|
||||
|
||||
|
||||
dependencies {
|
||||
implementation("com.expediagroup:graphql-kotlin-spring-server:5.3.0")
|
||||
implementation("org.jetbrains.kotlinx:kotlinx-datetime:0.3.1")
|
||||
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.3.1")
|
||||
|
||||
testImplementation("com.squareup.okhttp3:okhttp:4.9.3")
|
||||
|
||||
with(Deps.Log) {
|
||||
implementation(logback)
|
||||
}
|
||||
|
||||
implementation(project(":common"))
|
||||
}
|
||||
|
||||
kotlin {
|
||||
sourceSets.all {
|
||||
languageSettings {
|
||||
optIn("kotlin.RequiresOptIn")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
appengine {
|
||||
stage {
|
||||
setArtifact(tasks.named("bootJar").flatMap { (it as Jar).archiveFile })
|
||||
}
|
||||
deploy {
|
||||
projectId = "peopleinspace-graphql"
|
||||
version = "GCLOUD_CONFIG"
|
||||
}
|
||||
}
|
|
@ -1,3 +0,0 @@
|
|||
runtime: java11
|
||||
|
||||
entrypoint: java -Xmx64m -jar graphql-server.jar
|
|
@ -1,20 +0,0 @@
|
|||
package com.surrus.peopleinspace
|
||||
|
||||
import com.surrus.common.di.initKoin
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication
|
||||
import org.springframework.boot.runApplication
|
||||
import org.springframework.context.ConfigurableApplicationContext
|
||||
|
||||
|
||||
val koin = initKoin(enableNetworkLogs = true).koin
|
||||
|
||||
@SpringBootApplication
|
||||
class DefaultApplication {
|
||||
}
|
||||
|
||||
fun runServer(): ConfigurableApplicationContext {
|
||||
return runApplication<DefaultApplication>()
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -1,37 +0,0 @@
|
|||
package com.surrus.peopleinspace
|
||||
|
||||
import com.expediagroup.graphql.server.operations.Subscription
|
||||
import com.surrus.common.remote.IssPosition
|
||||
import com.surrus.common.remote.PeopleInSpaceApi
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.flow.flow
|
||||
import kotlinx.coroutines.reactive.asPublisher
|
||||
import org.reactivestreams.Publisher
|
||||
import org.slf4j.Logger
|
||||
import org.slf4j.LoggerFactory
|
||||
import org.springframework.stereotype.Component
|
||||
|
||||
|
||||
|
||||
@Component
|
||||
class IssPositionSubscription : Subscription {
|
||||
private val logger: Logger = LoggerFactory.getLogger(IssPositionSubscription::class.java)
|
||||
private var peopleInSpaceApi: PeopleInSpaceApi = koin.get()
|
||||
|
||||
|
||||
fun issPosition(): Publisher<IssPosition> {
|
||||
return flow {
|
||||
while (true) {
|
||||
val position = peopleInSpaceApi.fetchISSPosition().iss_position
|
||||
logger.info("ISS position = $position")
|
||||
emit(position)
|
||||
delay(POLL_INTERVAL)
|
||||
}
|
||||
}.asPublisher()
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val POLL_INTERVAL = 10000L
|
||||
}
|
||||
|
||||
}
|
|
@ -1,15 +0,0 @@
|
|||
package com.surrus.peopleinspace
|
||||
|
||||
import com.expediagroup.graphql.server.operations.Query
|
||||
import com.surrus.common.remote.PeopleInSpaceApi
|
||||
import com.surrus.common.remote.Assignment
|
||||
import org.springframework.stereotype.Component
|
||||
|
||||
data class People(val people: List<Assignment>)
|
||||
|
||||
@Component
|
||||
class RootQuery : Query {
|
||||
private var peopleInSpaceApi: PeopleInSpaceApi = koin.get()
|
||||
|
||||
suspend fun allPeople(): People = People(peopleInSpaceApi.fetchPeople().people)
|
||||
}
|
|
@ -1,6 +0,0 @@
|
|||
package com.surrus.peopleinspace
|
||||
|
||||
fun main(args: Array<String>) {
|
||||
runServer()
|
||||
}
|
||||
|
|
@ -1,2 +0,0 @@
|
|||
graphql:
|
||||
packages: "com.surrus"
|
|
@ -19,5 +19,3 @@ include(":app", ":common", ":compose-desktop")
|
|||
include(":wearApp")
|
||||
include(":web")
|
||||
include(":compose-web")
|
||||
include(":backend")
|
||||
include(":graphql-server")
|
||||
|
|
|
@ -6,7 +6,7 @@ import androidx.compose.ui.test.junit4.createComposeRule
|
|||
import androidx.compose.ui.test.onNodeWithTag
|
||||
import androidx.compose.ui.test.onNodeWithText
|
||||
import androidx.compose.ui.test.onParent
|
||||
import com.surrus.common.remote.Assignment
|
||||
import com.surrus.common.model.Assignment
|
||||
import com.surrus.peopleinspace.PersonListScreen
|
||||
import com.surrus.peopleinspace.PersonListTag
|
||||
import org.junit.Rule
|
||||
|
|
|
@ -14,7 +14,7 @@ import androidx.compose.ui.semantics.semantics
|
|||
import androidx.compose.ui.viewinterop.AndroidView
|
||||
import androidx.wear.compose.material.MaterialTheme
|
||||
import androidx.wear.compose.material.Scaffold
|
||||
import com.surrus.common.remote.IssPosition
|
||||
import com.surrus.common.model.IssPosition
|
||||
import org.koin.androidx.compose.getViewModel
|
||||
import org.osmdroid.tileprovider.tilesource.TileSourceFactory
|
||||
import org.osmdroid.util.GeoPoint
|
||||
|
|
|
@ -2,7 +2,7 @@ package com.surrus.peopleinspace
|
|||
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.surrus.common.remote.Assignment
|
||||
import com.surrus.common.model.Assignment
|
||||
import com.surrus.common.repository.PeopleInSpaceRepositoryInterface
|
||||
import kotlinx.coroutines.flow.SharingStarted
|
||||
import kotlinx.coroutines.flow.stateIn
|
||||
|
|
|
@ -34,7 +34,7 @@ import androidx.wear.compose.material.Scaffold
|
|||
import androidx.wear.compose.material.Text
|
||||
import androidx.wear.compose.material.Vignette
|
||||
import androidx.wear.compose.material.VignettePosition
|
||||
import com.surrus.common.remote.Assignment
|
||||
import com.surrus.common.model.Assignment
|
||||
import org.koin.androidx.compose.getViewModel
|
||||
|
||||
@Composable
|
||||
|
|
|
@ -47,7 +47,7 @@ import androidx.wear.compose.material.VignettePosition
|
|||
import androidx.wear.compose.material.rememberScalingLazyListState
|
||||
import coil.annotation.ExperimentalCoilApi
|
||||
import coil.compose.rememberImagePainter
|
||||
import com.surrus.common.remote.Assignment
|
||||
import com.surrus.common.model.Assignment
|
||||
import org.koin.androidx.compose.getViewModel
|
||||
|
||||
const val PersonListTag = "PersonList"
|
||||
|
|
|
@ -13,7 +13,7 @@ import androidx.glance.layout.padding
|
|||
import androidx.glance.text.FontWeight
|
||||
import androidx.glance.text.TextStyle
|
||||
import androidx.glance.unit.ColorProvider
|
||||
import com.surrus.common.remote.Assignment
|
||||
import com.surrus.common.model.Assignment
|
||||
import com.surrus.common.repository.PeopleInSpaceRepositoryInterface
|
||||
import com.surrus.peopleinspace.tile.util.BaseGlanceTileService
|
||||
import kotlinx.coroutines.flow.first
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import com.surrus.common.remote.Assignment
|
||||
import com.surrus.common.remote.IssPosition
|
||||
import com.surrus.common.model.Assignment
|
||||
import com.surrus.common.model.IssPosition
|
||||
import components.*
|
||||
import components.materialui.AppBar
|
||||
import components.materialui.Card
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
package components
|
||||
|
||||
import com.surrus.common.remote.IssPosition
|
||||
import com.surrus.common.model.IssPosition
|
||||
import components.pigeonmaps.Map
|
||||
import components.pigeonmaps.Marker
|
||||
import react.RBuilder
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
package components
|
||||
|
||||
import com.surrus.common.remote.Assignment
|
||||
import com.surrus.common.model.Assignment
|
||||
import components.materialui.Avatar
|
||||
import components.materialui.ListItem
|
||||
import components.materialui.ListItemAvatar
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
package components
|
||||
|
||||
import com.surrus.common.remote.Assignment
|
||||
import com.surrus.common.model.Assignment
|
||||
import kotlinx.css.Align
|
||||
import kotlinx.css.ObjectFit
|
||||
import kotlinx.css.alignSelf
|
||||
|
|
Loading…
Reference in a new issue